Merge pull request 'machines update: refactor - simplify' (#4506) from simplify-update into main

Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4506
This commit is contained in:
Mic92
2025-07-30 18:42:02 +00:00
16 changed files with 412 additions and 70 deletions

View File

@@ -2,7 +2,6 @@ import argparse
import contextlib
import logging
import sys
from pathlib import Path
from types import ModuleType
from clan_lib.custom_logger import setup_logging
@@ -37,13 +36,6 @@ with contextlib.suppress(ImportError):
import argcomplete # type: ignore[no-redef]
def flake_path(arg: str) -> str:
flake_dir = Path(arg).resolve()
if flake_dir.exists() and flake_dir.is_dir():
return str(flake_dir)
return arg
def default_flake() -> str | None:
val = get_clan_flake_toplevel_or_env()
if val:
@@ -98,7 +90,6 @@ def add_common_flags(parser: argparse.ArgumentParser) -> None:
help="path to the flake where the clan resides in, can be a remote flake or local, can be set through the [CLAN_DIR] environment variable",
default=default_flake(),
metavar="PATH",
type=flake_path,
)

View File

@@ -29,11 +29,11 @@ def get_clan_flake_toplevel_or_env() -> Path | None:
def get_clan_flake_toplevel() -> Path | None:
return find_toplevel([".clan-flake", ".git", ".hg", ".svn", "flake.nix"])
def find_git_repo_root() -> Path | None:
return find_toplevel([".git"])
try:
return find_toplevel([".clan-flake", ".git", ".hg", ".svn", "flake.nix"])
except OSError:
# We might run clan from a directory that is not readable.
return None
def clan_key_safe(flake_url: str) -> str:

View File

@@ -119,6 +119,7 @@ def run_machine_update(
machine: The Machine instance to deploy.
target_host: Remote object representing the target host for deployment.
build_host: Optional Remote object representing the build host.
force_fetch_local: Whether to fetch flake inputs locally before uploading.
Raises:
ClanError: If the machine is not found in the inventory or if there are issues with
generating facts or variables.
@@ -127,21 +128,24 @@ def run_machine_update(
with ExitStack() as stack:
target_host = stack.enter_context(target_host.ssh_control_master())
if build_host:
# If no build host is specified, use the target host as the build host.
if build_host is None:
build_host = target_host
else:
build_host = stack.enter_context(build_host.ssh_control_master())
sudo_host = stack.enter_context(target_host.become_root())
# Some operations require root privileges on the target host.
target_host_root = stack.enter_context(target_host.become_root())
generate_facts([machine], service=None, regenerate=False)
generate_vars([machine], generator_name=None, regenerate=False)
upload_secrets(machine, sudo_host)
upload_secret_vars(machine, sudo_host)
# Upload secrets to the target host using root
upload_secrets(machine, target_host_root)
upload_secret_vars(machine, target_host_root)
if build_host:
path = upload_sources(machine, build_host, force_fetch_local)
else:
path = upload_sources(machine, target_host, force_fetch_local)
# Upload the flake's source to the build host.
path = upload_sources(machine, build_host, force_fetch_local)
nix_options = machine.flake.nix_options if machine.flake.nix_options else []
@@ -159,8 +163,6 @@ def run_machine_update(
f"{path}#{machine.name}",
]
become_root = True
if machine._class_ == "nixos":
nix_options += [
"--fast",
@@ -168,8 +170,7 @@ def run_machine_update(
"",
]
if build_host:
become_root = False
if build_host != target_host:
nix_options += ["--target-host", target_host.target]
if target_host.user != "root":
@@ -183,13 +184,14 @@ def run_machine_update(
*nix_options,
]
if become_root and not build_host:
target_host = sudo_host
# If we build on the target host, we need to become root for building.
# We are not using --use-remote-sudo here, so that our sudo ask proxy work: https://git.clan.lol/clan/clan-core/pulls/3642
# We can't do that yet, when a build host is specified.
if build_host == target_host:
build_host = target_host_root
deploy_host = build_host if build_host else target_host
remote_env = deploy_host.nix_ssh_env(control_master=False)
ret = deploy_host.run(
remote_env = build_host.nix_ssh_env(control_master=False)
ret = build_host.run(
switch_cmd,
RunOpts(
check=False,
@@ -224,7 +226,7 @@ def run_machine_update(
machine.info(
"Mobile machine detected, applying workaround deployment method"
)
ret = deploy_host.run(
ret = build_host.run(
["nixos--rebuild", "test", *nix_options] if is_mobile else switch_cmd,
RunOpts(
log=Log.BOTH,

View File

@@ -12,7 +12,7 @@ from tempfile import NamedTemporaryFile
from typing import Any, override
from clan_cli.vars.generate import generate_vars
from clan_lib.dirs import find_git_repo_root
from clan_lib.dirs import find_toplevel
from clan_lib.flake.flake import Flake
from clan_lib.machines.machines import Machine
from clan_lib.nix import nix_config, nix_eval, nix_test_store
@@ -139,7 +139,7 @@ def parse_args() -> Options:
i.e. 'nix eval <repo_root>#checks ...'
""",
required=False,
default=os.environ.get("PRJ_ROOT", find_git_repo_root()),
default=os.environ.get("PRJ_ROOT", find_toplevel([".git"])),
)
parser.add_argument(
"--clean",

View File

@@ -75,7 +75,7 @@ def setup_nix_in_nix(closure_info: str | None) -> None:
def prepare_test_flake(
temp_dir: str, clan_core_for_checks: str, closure_info: str
) -> str:
) -> Path:
"""Set up Nix store and copy test flake to temporary directory
Args:
@@ -93,4 +93,4 @@ def prepare_test_flake(
flake_dir = Path(temp_dir) / "test-flake"
subprocess.run(["cp", "-r", clan_core_for_checks, flake_dir], check=True) # noqa: S603, S607
subprocess.run(["chmod", "-R", "+w", flake_dir], check=True) # noqa: S603, S607
return str(flake_dir)
return flake_dir

View File

@@ -27,10 +27,15 @@ def setup_ssh_connection(
SSHConnection with host_port and ssh_key path
"""
host_port = find_free_port()
target.wait_for_unit("sshd.service")
target.wait_for_open_port(22)
setup_port_forwarding(target, host_port)
# Check if this is a container test (doesn't have forward_port method)
if hasattr(target, "forward_port"):
setup_port_forwarding(target, host_port)
else:
# In container tests, we use the host network namespace
# so we can connect directly to the container's SSH port
host_port = 22
ssh_key = Path(temp_dir) / "id_ed25519"
with ssh_key.open("w") as f, Path(assets_ssh_privkey).open() as src: