diff --git a/pkgs/clan-cli/clan_cli/__init__.py b/pkgs/clan-cli/clan_cli/__init__.py index 2ee9db6b8..0f42ab0a2 100644 --- a/pkgs/clan-cli/clan_cli/__init__.py +++ b/pkgs/clan-cli/clan_cli/__init__.py @@ -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 diff --git a/pkgs/clan-cli/clan_cli/async_cmd.py b/pkgs/clan-cli/clan_cli/async_cmd.py index 06abb8a8a..462b2000f 100644 --- a/pkgs/clan-cli/clan_cli/async_cmd.py +++ b/pkgs/clan-cli/clan_cli/async_cmd.py @@ -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: diff --git a/pkgs/clan-cli/clan_cli/clan_modules.py b/pkgs/clan-cli/clan_cli/clan_modules.py index 54d0900d9..e135b2cd9 100644 --- a/pkgs/clan-cli/clan_cli/clan_modules.py +++ b/pkgs/clan-cli/clan_cli/clan_modules.py @@ -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 diff --git a/pkgs/clan-cli/clan_cli/config/__init__.py b/pkgs/clan-cli/clan_cli/config/__init__.py index 0cd5ab4fc..e76c3d8a7 100644 --- a/pkgs/clan-cli/clan_cli/config/__init__.py +++ b/pkgs/clan-cli/clan_cli/config/__init__.py @@ -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: diff --git a/pkgs/clan-cli/clan_cli/config/machine.py b/pkgs/clan-cli/clan_cli/config/machine.py index 30ded26dd..754b5380f 100644 --- a/pkgs/clan-cli/clan_cli/config/machine.py +++ b/pkgs/clan-cli/clan_cli/config/machine.py @@ -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}(? dict: - flake = specific_flake_dir(flake_name) # use nix eval to lib.evalModules .#nixosConfigurations..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: diff --git a/pkgs/clan-cli/clan_cli/dirs.py b/pkgs/clan-cli/clan_cli/dirs.py index be8beb59d..e5701b3e1 100644 --- a/pkgs/clan-cli/clan_cli/dirs.py +++ b/pkgs/clan-cli/clan_cli/dirs.py @@ -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: diff --git a/pkgs/clan-cli/clan_cli/flakes/__init__.py b/pkgs/clan-cli/clan_cli/flakes/__init__.py index fb2dcaae9..a24e84d11 100644 --- a/pkgs/clan-cli/clan_cli/flakes/__init__.py +++ b/pkgs/clan-cli/clan_cli/flakes/__init__.py @@ -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) diff --git a/pkgs/clan-cli/clan_cli/flakes/create.py b/pkgs/clan-cli/clan_cli/flakes/create.py index f5e7e1957..b4d642310 100644 --- a/pkgs/clan-cli/clan_cli/flakes/create.py +++ b/pkgs/clan-cli/clan_cli/flakes/create.py @@ -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) diff --git a/pkgs/clan-cli/clan_cli/flakes/list_flakes.py b/pkgs/clan-cli/clan_cli/flakes/list_flakes.py deleted file mode 100644 index bec34ef50..000000000 --- a/pkgs/clan-cli/clan_cli/flakes/list_flakes.py +++ /dev/null @@ -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) diff --git a/pkgs/clan-cli/clan_cli/machines/create.py b/pkgs/clan-cli/clan_cli/machines/create.py index 1bb5bc4ae..711735eb3 100644 --- a/pkgs/clan-cli/clan_cli/machines/create.py +++ b/pkgs/clan-cli/clan_cli/machines/create.py @@ -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) diff --git a/pkgs/clan-cli/clan_cli/machines/delete.py b/pkgs/clan-cli/clan_cli/machines/delete.py index e772edc66..347ab65fd 100644 --- a/pkgs/clan-cli/clan_cli/machines/delete.py +++ b/pkgs/clan-cli/clan_cli/machines/delete.py @@ -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) diff --git a/pkgs/clan-cli/clan_cli/machines/facts.py b/pkgs/clan-cli/clan_cli/machines/facts.py index 3f148ccf6..5d2df53a5 100644 --- a/pkgs/clan-cli/clan_cli/machines/facts.py +++ b/pkgs/clan-cli/clan_cli/machines/facts.py @@ -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() diff --git a/pkgs/clan-cli/clan_cli/machines/install.py b/pkgs/clan-cli/clan_cli/machines/install.py index 64c2b71b0..d3a10f43e 100644 --- a/pkgs/clan-cli/clan_cli/machines/install.py +++ b/pkgs/clan-cli/clan_cli/machines/install.py @@ -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) diff --git a/pkgs/clan-cli/clan_cli/machines/list.py b/pkgs/clan-cli/clan_cli/machines/list.py index 079bcd739..371be2c8f 100644 --- a/pkgs/clan-cli/clan_cli/machines/list.py +++ b/pkgs/clan-cli/clan_cli/machines/list.py @@ -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) diff --git a/pkgs/clan-cli/clan_cli/types.py b/pkgs/clan-cli/clan_cli/types.py index 43adfe723..9f4f04df7 100644 --- a/pkgs/clan-cli/clan_cli/types.py +++ b/pkgs/clan-cli/clan_cli/types.py @@ -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 diff --git a/pkgs/clan-cli/clan_cli/vms/create.py b/pkgs/clan-cli/clan_cli/vms/create.py index 5d4f9f3a5..ce4f101cc 100644 --- a/pkgs/clan-cli/clan_cli/vms/create.py +++ b/pkgs/clan-cli/clan_cli/vms/create.py @@ -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) diff --git a/pkgs/clan-cli/clan_cli/vms/inspect.py b/pkgs/clan-cli/clan_cli/vms/inspect.py index 74722235f..fa32439db 100644 --- a/pkgs/clan-cli/clan_cli/vms/inspect.py +++ b/pkgs/clan-cli/clan_cli/vms/inspect.py @@ -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) diff --git a/pkgs/clan-cli/clan_cli/webui/api_inputs.py b/pkgs/clan-cli/clan_cli/webui/api_inputs.py index be3cd0990..ba17360e4 100644 --- a/pkgs/clan-cli/clan_cli/webui/api_inputs.py +++ b/pkgs/clan-cli/clan_cli/webui/api_inputs.py @@ -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 diff --git a/pkgs/clan-cli/clan_cli/webui/routers/clan_modules.py b/pkgs/clan-cli/clan_cli/webui/routers/clan_modules.py index 42cd98e86..89c6cc41b 100644 --- a/pkgs/clan-cli/clan_cli/webui/routers/clan_modules.py +++ b/pkgs/clan-cli/clan_cli/webui/routers/clan_modules.py @@ -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) diff --git a/pkgs/clan-cli/clan_cli/webui/routers/flake.py b/pkgs/clan-cli/clan_cli/webui/routers/flake.py index 790cc172c..b8bbec767 100644 --- a/pkgs/clan-cli/clan_cli/webui/routers/flake.py +++ b/pkgs/clan-cli/clan_cli/webui/routers/flake.py @@ -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) diff --git a/pkgs/clan-cli/clan_cli/webui/routers/machines.py b/pkgs/clan-cli/clan_cli/webui/routers/machines.py index 55a142a10..0de5b6fb0 100644 --- a/pkgs/clan-cli/clan_cli/webui/routers/machines.py +++ b/pkgs/clan-cli/clan_cli/webui/routers/machines.py @@ -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) diff --git a/pkgs/clan-cli/tests/fixtures_flakes.py b/pkgs/clan-cli/tests/fixtures_flakes.py index c50b382b7..b48e44485 100644 --- a/pkgs/clan-cli/tests/fixtures_flakes.py +++ b/pkgs/clan-cli/tests/fixtures_flakes.py @@ -13,7 +13,6 @@ from pydantic.tools import parse_obj_as from root import CLAN_CORE from clan_cli.dirs import nixpkgs_source -from clan_cli.types import FlakeName log = logging.getLogger(__name__) @@ -36,14 +35,13 @@ def substitute( class FlakeForTest(NamedTuple): - name: FlakeName path: Path def create_flake( monkeypatch: pytest.MonkeyPatch, temporary_home: Path, - flake_name: FlakeName, + flake_name: str, clan_core_flake: Path | None = None, machines: list[str] = [], remote: bool = False, @@ -55,7 +53,7 @@ def create_flake( template = Path(__file__).parent / flake_name # copy the template to a new temporary location - flake = temporary_home / ".config/clan/flakes" / flake_name + flake = temporary_home / flake_name shutil.copytree(template, flake) # lookup the requested machines in ./test_machines and include them @@ -91,16 +89,16 @@ def create_flake( if remote: with tempfile.TemporaryDirectory(): - yield FlakeForTest(flake_name, flake) + yield FlakeForTest(flake) else: - yield FlakeForTest(flake_name, flake) + yield FlakeForTest(flake) @pytest.fixture def test_flake( monkeypatch: pytest.MonkeyPatch, temporary_home: Path ) -> Iterator[FlakeForTest]: - yield from create_flake(monkeypatch, temporary_home, FlakeName("test_flake")) + yield from create_flake(monkeypatch, temporary_home, "test_flake") @pytest.fixture @@ -114,7 +112,7 @@ def test_flake_with_core( yield from create_flake( monkeypatch, temporary_home, - FlakeName("test_flake_with_core"), + "test_flake_with_core", CLAN_CORE, ) @@ -140,6 +138,6 @@ def test_flake_with_core_and_pass( yield from create_flake( monkeypatch, temporary_home, - FlakeName("test_flake_with_core_and_pass"), + "test_flake_with_core_and_pass", CLAN_CORE, ) diff --git a/pkgs/clan-cli/tests/helpers/cli.py b/pkgs/clan-cli/tests/helpers/cli.py index 83bbc1166..44fc25d55 100644 --- a/pkgs/clan-cli/tests/helpers/cli.py +++ b/pkgs/clan-cli/tests/helpers/cli.py @@ -4,22 +4,17 @@ import shlex from clan_cli import create_parser from clan_cli.custom_logger import get_caller -from clan_cli.dirs import get_clan_flake_toplevel log = logging.getLogger(__name__) class Cli: - def __init__(self) -> None: - self.parser = create_parser(prog="clan") - def run(self, args: list[str]) -> argparse.Namespace: + parser = create_parser(prog="clan") cmd = shlex.join(["clan"] + args) log.debug(f"$ {cmd}") log.debug(f"Caller {get_caller()}") - parsed = self.parser.parse_args(args) - if parsed.flake is None: - parsed.flake = get_clan_flake_toplevel() + parsed = parser.parse_args(args) if hasattr(parsed, "func"): parsed.func(parsed) return parsed diff --git a/pkgs/clan-cli/tests/test_clan_modules.py b/pkgs/clan-cli/tests/test_clan_modules.py index 300a7adcd..b9a091feb 100644 --- a/pkgs/clan-cli/tests/test_clan_modules.py +++ b/pkgs/clan-cli/tests/test_clan_modules.py @@ -6,9 +6,9 @@ from fixtures_flakes import FlakeForTest @pytest.mark.with_core def test_configure_machine(api: TestClient, test_flake_with_core: FlakeForTest) -> None: # retrieve the list of available clanModules - response = api.get(f"/api/{test_flake_with_core.name}/clan_modules") + response = api.get(f"/api/clan_modules?flake_dir={test_flake_with_core.path}") + assert response.status_code == 200, response.text response_json = response.json() - assert response.status_code == 200 assert isinstance(response_json, dict) assert "clan_modules" in response_json assert len(response_json["clan_modules"]) > 0 diff --git a/pkgs/clan-cli/tests/test_config.py b/pkgs/clan-cli/tests/test_config.py index 9dabf425f..8a8c71bcc 100644 --- a/pkgs/clan-cli/tests/test_config.py +++ b/pkgs/clan-cli/tests/test_config.py @@ -39,6 +39,8 @@ def test_set_some_option( cli = Cli() cli.run( [ + "--flake", + str(test_flake.path), "config", "--quiet", "--options-file", @@ -47,7 +49,6 @@ def test_set_some_option( out_file.name, ] + args - + [test_flake.name] ) json_out = json.loads(open(out_file.name).read()) assert json_out == expected @@ -61,11 +62,30 @@ def test_configure_machine( ) -> None: cli = Cli() - cli.run(["config", "-m", "machine1", "clan.jitsi.enable", "true", test_flake.name]) + cli.run( + [ + "--flake", + str(test_flake.path), + "config", + "-m", + "machine1", + "clan.jitsi.enable", + "true", + ] + ) # clear the output buffer capsys.readouterr() # read a option value - cli.run(["config", "-m", "machine1", "clan.jitsi.enable", test_flake.name]) + cli.run( + [ + "--flake", + str(test_flake.path), + "config", + "-m", + "machine1", + "clan.jitsi.enable", + ] + ) # read the output assert capsys.readouterr().out == "true\n" diff --git a/pkgs/clan-cli/tests/test_create_flake.py b/pkgs/clan-cli/tests/test_create_flake.py index 8cacf12f0..bff689db2 100644 --- a/pkgs/clan-cli/tests/test_create_flake.py +++ b/pkgs/clan-cli/tests/test_create_flake.py @@ -6,7 +6,6 @@ import pytest from api import TestClient from cli import Cli -from clan_cli.dirs import clan_flakes_dir from clan_cli.flakes.create import DEFAULT_URL @@ -19,13 +18,11 @@ def cli() -> Cli: def test_create_flake_api( monkeypatch: pytest.MonkeyPatch, api: TestClient, temporary_home: Path ) -> None: - monkeypatch.chdir(clan_flakes_dir()) - flake_name = "flake_dir" - flake_dir = clan_flakes_dir() / flake_name + flake_dir = temporary_home / "test-flake" response = api.post( - "/api/flake/create", + f"/api/flake/create?flake_dir={flake_dir}", json=dict( - flake_name=str(flake_dir), + flake_dir=str(flake_dir), url=str(DEFAULT_URL), ), ) @@ -42,17 +39,15 @@ def test_create_flake( temporary_home: Path, cli: Cli, ) -> None: - monkeypatch.chdir(clan_flakes_dir()) - flake_name = "flake_dir" - flake_dir = clan_flakes_dir() / flake_name + flake_dir = temporary_home / "test-flake" - cli.run(["flakes", "create", flake_name]) + cli.run(["flakes", "create", str(flake_dir)]) assert (flake_dir / ".clan-flake").exists() monkeypatch.chdir(flake_dir) - cli.run(["machines", "create", "machine1", flake_name]) + cli.run(["machines", "create", "machine1"]) capsys.readouterr() # flush cache - cli.run(["machines", "list", flake_name]) + cli.run(["machines", "list"]) assert "machine1" in capsys.readouterr().out flake_show = subprocess.run( ["nix", "flake", "show", "--json"], @@ -67,9 +62,7 @@ def test_create_flake( pytest.fail("nixosConfigurations.machine1 not found in flake outputs") # configure machine1 capsys.readouterr() - cli.run( - ["config", "--machine", "machine1", "services.openssh.enable", "", flake_name] - ) + cli.run(["config", "--machine", "machine1", "services.openssh.enable", ""]) capsys.readouterr() cli.run( [ @@ -78,6 +71,5 @@ def test_create_flake( "machine1", "services.openssh.enable", "true", - flake_name, ] ) diff --git a/pkgs/clan-cli/tests/test_flake_api.py b/pkgs/clan-cli/tests/test_flake_api.py index b05e057e9..7bca5fbc1 100644 --- a/pkgs/clan-cli/tests/test_flake_api.py +++ b/pkgs/clan-cli/tests/test_flake_api.py @@ -8,15 +8,6 @@ from fixtures_flakes import FlakeForTest log = logging.getLogger(__name__) -@pytest.mark.impure -def test_list_flakes(api: TestClient, test_flake_with_core: FlakeForTest) -> None: - response = api.get("/api/flake/list") - assert response.status_code == 200, "Failed to list flakes" - data = response.json() - print("Data: ", data) - assert data.get("flakes") == ["test_flake_with_core"] - - @pytest.mark.impure def test_inspect_ok(api: TestClient, test_flake_with_core: FlakeForTest) -> None: params = {"url": str(test_flake_with_core.path)} diff --git a/pkgs/clan-cli/tests/test_machines_api.py b/pkgs/clan-cli/tests/test_machines_api.py index ce17d3815..eb6dbd69b 100644 --- a/pkgs/clan-cli/tests/test_machines_api.py +++ b/pkgs/clan-cli/tests/test_machines_api.py @@ -3,19 +3,20 @@ from api import TestClient from fixtures_flakes import FlakeForTest -def test_create_and_list(api: TestClient, test_flake: FlakeForTest) -> None: - response = api.get(f"/api/{test_flake.name}/machines") +def test_machines(api: TestClient, test_flake: FlakeForTest) -> None: + response = api.get(f"/api/machines?flake_dir={test_flake.path}") assert response.status_code == 200 assert response.json() == {"machines": []} - - response = api.put(f"/api/{test_flake.name}/machines/test/config", json=dict()) + response = api.put( + f"/api/machines/test/config?flake_dir={test_flake.path}", json={} + ) assert response.status_code == 200 - response = api.get(f"/api/{test_flake.name}/machines/test") + response = api.get(f"/api/machines/test?flake_dir={test_flake.path}") assert response.status_code == 200 assert response.json() == {"machine": {"name": "test", "status": "unknown"}} - response = api.get(f"/api/{test_flake.name}/machines") + response = api.get(f"/api/machines?flake_dir={test_flake.path}") assert response.status_code == 200 assert response.json() == {"machines": [{"name": "test", "status": "unknown"}]} @@ -24,7 +25,7 @@ def test_create_and_list(api: TestClient, test_flake: FlakeForTest) -> None: def test_schema_errors(api: TestClient, test_flake_with_core: FlakeForTest) -> None: # make sure that eval errors do not raise an internal server error response = api.put( - f"/api/{test_flake_with_core.name}/schema", + f"/api/schema?flake_dir={test_flake_with_core.path}", json={"imports": ["some-invalid-import"]}, ) assert response.status_code == 422 @@ -39,7 +40,7 @@ def test_schema_invalid_clan_imports( api: TestClient, test_flake_with_core: FlakeForTest ) -> None: response = api.put( - f"/api/{test_flake_with_core.name}/schema", + f"/api/schema?flake_dir={test_flake_with_core.path}", json={"clanImports": ["non-existing-clan-module"]}, ) assert response.status_code == 400 @@ -54,7 +55,8 @@ def test_create_machine_invalid_hostname( api: TestClient, test_flake: FlakeForTest ) -> None: response = api.put( - f"/api/{test_flake.name}/machines/-invalid-hostname/config", json=dict() + f"/api/machines/-invalid-hostname/config?flake_dir={test_flake.path}", + json=dict(), ) assert response.status_code == 422 assert ( @@ -67,7 +69,7 @@ def test_verify_config_without_machine( api: TestClient, test_flake_with_core: FlakeForTest ) -> None: response = api.put( - f"/api/{test_flake_with_core.name}/machines/test/verify", + f"/api/machines/test/verify?flake_dir={test_flake_with_core.path}", json=dict(), ) assert response.status_code == 200 @@ -79,12 +81,14 @@ def test_ensure_empty_config_is_valid( api: TestClient, test_flake_with_core: FlakeForTest ) -> None: response = api.put( - f"/api/{test_flake_with_core.name}/machines/test/config", + f"/api/machines/test/config?flake_dir={test_flake_with_core.path}", json=dict(), ) assert response.status_code == 200 - response = api.get(f"/api/{test_flake_with_core.name}/machines/test/verify") + response = api.get( + f"/api/machines/test/verify?flake_dir={test_flake_with_core.path}" + ) assert response.status_code == 200 assert response.json() == {"success": True, "error": None} @@ -92,17 +96,21 @@ def test_ensure_empty_config_is_valid( @pytest.mark.with_core def test_configure_machine(api: TestClient, test_flake_with_core: FlakeForTest) -> None: # ensure error 404 if machine does not exist when accessing the config - response = api.get(f"/api/{test_flake_with_core.name}/machines/machine1/config") + response = api.get( + f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}" + ) assert response.status_code == 404 # create the machine response = api.put( - f"/api/{test_flake_with_core.name}/machines/machine1/config", json={} + f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}", json={} ) 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") + response = api.get( + f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}" + ) assert response.status_code == 200 assert response.json() == { "clanImports": [], @@ -111,7 +119,7 @@ def test_configure_machine(api: TestClient, test_flake_with_core: FlakeForTest) # get jsonschema for without imports response = api.put( - f"/api/{test_flake_with_core.name}/schema", + f"/api/schema?flake_dir={test_flake_with_core.path}", json={"clanImports": []}, ) assert response.status_code == 200 @@ -133,7 +141,7 @@ def test_configure_machine(api: TestClient, test_flake_with_core: FlakeForTest) # verify an invalid config (foo option does not exist) response = api.put( - f"/api/{test_flake_with_core.name}/machines/machine1/verify", + f"/api/machines/machine1/verify?flake_dir={test_flake_with_core.path}", json=invalid_config, ) assert response.status_code == 200 @@ -141,13 +149,15 @@ def test_configure_machine(api: TestClient, test_flake_with_core: FlakeForTest) # set come invalid config (foo option does not exist) response = api.put( - f"/api/{test_flake_with_core.name}/machines/machine1/config", + f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}", json=invalid_config, ) assert response.status_code == 200 # ensure the config has actually been updated - response = api.get(f"/api/{test_flake_with_core.name}/machines/machine1/config") + response = api.get( + f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}" + ) assert response.status_code == 200 assert response.json() == dict(clanImports=[], **invalid_config) @@ -162,20 +172,22 @@ def test_configure_machine(api: TestClient, test_flake_with_core: FlakeForTest) ) response = api.put( - f"/api/{test_flake_with_core.name}/machines/machine1/config", + f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}", json=config2, ) assert response.status_code == 200 # ensure the config has been applied response = api.get( - f"/api/{test_flake_with_core.name}/machines/machine1/config", + f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}", ) assert response.status_code == 200 assert response.json() == dict(clanImports=[], **config2) # get the config again - response = api.get(f"/api/{test_flake_with_core.name}/machines/machine1/config") + response = api.get( + f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}" + ) assert response.status_code == 200 assert response.json() == {"clanImports": [], **config2} @@ -183,27 +195,29 @@ def test_configure_machine(api: TestClient, test_flake_with_core: FlakeForTest) # For example, this should not result in the boot.loader.grub.devices being # set twice (eg. merged) response = api.put( - f"/api/{test_flake_with_core.name}/machines/machine1/config", + f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}", json=config2, ) assert response.status_code == 200 # ensure the config has been applied response = api.get( - f"/api/{test_flake_with_core.name}/machines/machine1/config", + f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}", ) assert response.status_code == 200 assert response.json() == dict(clanImports=[], **config2) # verify the machine config evaluates - response = api.get(f"/api/{test_flake_with_core.name}/machines/machine1/verify") + response = api.get( + f"/api/machines/machine1/verify?flake_dir={test_flake_with_core.path}" + ) assert response.status_code == 200 assert response.json() == {"success": True, "error": None} # get the schema with an extra module imported response = api.put( - f"/api/{test_flake_with_core.name}/schema", + f"/api/schema?flake_dir={test_flake_with_core.path}", json={"clanImports": ["diskLayouts"]}, ) # expect the result schema to contain the deltachat option @@ -227,14 +241,14 @@ def test_configure_machine(api: TestClient, test_flake_with_core: FlakeForTest) # set the fake-module.fake-flag option to true response = api.put( - f"/api/{test_flake_with_core.name}/machines/machine1/config", + f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}", json=config_with_imports, ) assert response.status_code == 200 # ensure the config has been applied response = api.get( - f"/api/{test_flake_with_core.name}/machines/machine1/config", + f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}", ) assert response.status_code == 200 assert response.json() == { @@ -248,7 +262,7 @@ def test_configure_machine(api: TestClient, test_flake_with_core: FlakeForTest) # remove the import from the config response = api.put( - f"/api/{test_flake_with_core.name}/machines/machine1/config", + f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}", json=dict( clanImports=[], ), @@ -257,7 +271,7 @@ def test_configure_machine(api: TestClient, test_flake_with_core: FlakeForTest) # ensure the config has been applied response = api.get( - f"/api/{test_flake_with_core.name}/machines/machine1/config", + f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}", ) assert response.status_code == 200 assert response.json() == { diff --git a/pkgs/clan-cli/tests/test_machines_cli.py b/pkgs/clan-cli/tests/test_machines_cli.py index 4183441f9..8ee515bd8 100644 --- a/pkgs/clan-cli/tests/test_machines_cli.py +++ b/pkgs/clan-cli/tests/test_machines_cli.py @@ -7,16 +7,16 @@ def test_machine_subcommands( test_flake: FlakeForTest, capsys: pytest.CaptureFixture ) -> None: cli = Cli() - cli.run(["machines", "create", "machine1", test_flake.name]) + cli.run(["--flake", str(test_flake.path), "machines", "create", "machine1"]) capsys.readouterr() - cli.run(["machines", "list", test_flake.name]) + cli.run(["--flake", str(test_flake.path), "machines", "list"]) out = capsys.readouterr() assert "machine1\n" == out.out - cli.run(["machines", "delete", "machine1", test_flake.name]) + cli.run(["--flake", str(test_flake.path), "machines", "delete", "machine1"]) capsys.readouterr() - cli.run(["machines", "list", test_flake.name]) + cli.run(["--flake", str(test_flake.path), "machines", "list"]) out = capsys.readouterr() assert "" == out.out diff --git a/pkgs/clan-cli/tests/test_machines_config.py b/pkgs/clan-cli/tests/test_machines_config.py index 2ce8980d8..a1d02cf52 100644 --- a/pkgs/clan-cli/tests/test_machines_config.py +++ b/pkgs/clan-cli/tests/test_machines_config.py @@ -6,5 +6,5 @@ from clan_cli.config.schema import machine_schema @pytest.mark.with_core def test_schema_for_machine(test_flake_with_core: FlakeForTest) -> None: - schema = machine_schema(test_flake_with_core.name, config={}) + schema = machine_schema(test_flake_with_core.path, config={}) assert "properties" in schema diff --git a/pkgs/clan-cli/tests/test_secrets_generate.py b/pkgs/clan-cli/tests/test_secrets_generate.py index 9857174b8..94b6f8363 100644 --- a/pkgs/clan-cli/tests/test_secrets_generate.py +++ b/pkgs/clan-cli/tests/test_secrets_generate.py @@ -38,7 +38,7 @@ def test_generate_secret( has_secret(test_flake_with_core.path, "vm1-zerotier-identity-secret") has_secret(test_flake_with_core.path, "vm1-zerotier-subnet") network_id = machine_get_fact( - test_flake_with_core.name, "vm1", "zerotier-network-id" + test_flake_with_core.path, "vm1", "zerotier-network-id" ) assert len(network_id) == 16 secrets_folder = sops_secrets_folder(test_flake_with_core.path) @@ -59,7 +59,7 @@ def test_generate_secret( cli.run(["secrets", "generate", "vm2"]) assert has_secret(test_flake_with_core.path, "vm2-age.key") assert has_secret(test_flake_with_core.path, "vm2-zerotier-identity-secret") - ip = machine_get_fact(test_flake_with_core.name, "vm1", "zerotier-ip") + ip = machine_get_fact(test_flake_with_core.path, "vm1", "zerotier-ip") assert ipaddress.IPv6Address(ip).is_private - meshname = machine_get_fact(test_flake_with_core.name, "vm1", "zerotier-meshname") + meshname = machine_get_fact(test_flake_with_core.path, "vm1", "zerotier-meshname") assert len(meshname) == 26 diff --git a/pkgs/clan-cli/tests/test_secrets_password_store.py b/pkgs/clan-cli/tests/test_secrets_password_store.py index d0c01f3f1..3e32278c5 100644 --- a/pkgs/clan-cli/tests/test_secrets_password_store.py +++ b/pkgs/clan-cli/tests/test_secrets_password_store.py @@ -41,7 +41,7 @@ def test_upload_secret( subprocess.run(nix_shell(["pass"], ["pass", "init", "test@local"]), check=True) cli.run(["secrets", "generate", "vm1"]) network_id = machine_get_fact( - test_flake_with_core_and_pass.name, "vm1", "zerotier-network-id" + test_flake_with_core_and_pass.path, "vm1", "zerotier-network-id" ) assert len(network_id) == 16 identity_secret = ( diff --git a/pkgs/clan-cli/tests/test_vms_api_create.py b/pkgs/clan-cli/tests/test_vms_api_create.py index 28106fb9a..f5b955789 100644 --- a/pkgs/clan-cli/tests/test_vms_api_create.py +++ b/pkgs/clan-cli/tests/test_vms_api_create.py @@ -10,8 +10,6 @@ from httpx import SyncByteStream from pydantic import AnyUrl from root import CLAN_CORE -from clan_cli.types import FlakeName - if TYPE_CHECKING: from age_keys import KeyPair @@ -23,7 +21,7 @@ def flake_with_vm_with_secrets( yield from create_flake( monkeypatch, temporary_home, - FlakeName("test_flake_with_core_dynamic_machines"), + "test_flake_with_core_dynamic_machines", CLAN_CORE, machines=["vm_with_secrets"], ) @@ -36,7 +34,7 @@ def remote_flake_with_vm_without_secrets( yield from create_flake( monkeypatch, temporary_home, - FlakeName("test_flake_with_core_dynamic_machines"), + "test_flake_with_core_dynamic_machines", CLAN_CORE, machines=["vm_without_secrets"], remote=True, diff --git a/pkgs/clan-cli/tests/test_vms_cli.py b/pkgs/clan-cli/tests/test_vms_cli.py index 4cd17d14a..00153b478 100644 --- a/pkgs/clan-cli/tests/test_vms_cli.py +++ b/pkgs/clan-cli/tests/test_vms_cli.py @@ -16,7 +16,7 @@ def test_inspect( test_flake_with_core: FlakeForTest, capsys: pytest.CaptureFixture ) -> None: cli = Cli() - cli.run(["vms", "inspect", "vm1", test_flake_with_core.name]) + cli.run(["--flake", str(test_flake_with_core.path), "vms", "inspect", "vm1"]) out = capsys.readouterr() # empty the buffer assert "Cores" in out.out diff --git a/pkgs/ui/flake-module.nix b/pkgs/ui/flake-module.nix index 12335ec03..5708dccfc 100644 --- a/pkgs/ui/flake-module.nix +++ b/pkgs/ui/flake-module.nix @@ -7,7 +7,8 @@ in { packages = { - ui = base.pkg.global; + # disabled because frontend is broken after cli refactoring + # ui = base.pkg.global; ui-assets = pkgs.callPackage ./nix/ui-assets.nix { }; # EXAMPLE: GITEA_TOKEN=$(rbw get -f GITEA_TOKEN git.clan.lol) nix run .#update-ui-assets update-ui-assets = pkgs.callPackage ./nix/update-ui-assets.nix { };