Merge pull request 'cleanup_install' (#4373) from Qubasa/clan-core:cleanup_install into main

Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4373
This commit is contained in:
DavHau
2025-07-16 09:18:09 +00:00
committed by pinpox
7 changed files with 35 additions and 28 deletions

View File

@@ -1,6 +1,7 @@
import argparse import argparse
import logging import logging
from pathlib import Path from pathlib import Path
from typing import get_args
from clan_lib.flake import require_flake from clan_lib.flake import require_flake
from clan_lib.machines.hardware import ( from clan_lib.machines.hardware import (
@@ -10,6 +11,7 @@ from clan_lib.machines.hardware import (
) )
from clan_lib.machines.machines import Machine from clan_lib.machines.machines import Machine
from clan_lib.machines.suggestions import validate_machine_names from clan_lib.machines.suggestions import validate_machine_names
from clan_lib.ssh.host_key import HostKeyCheck
from clan_lib.ssh.remote import Remote from clan_lib.ssh.remote import Remote
from clan_cli.completions import add_dynamic_completer, complete_machines from clan_cli.completions import add_dynamic_completer, complete_machines
@@ -59,7 +61,7 @@ def register_update_hardware_config(parser: argparse.ArgumentParser) -> None:
) )
parser.add_argument( parser.add_argument(
"--host-key-check", "--host-key-check",
choices=["strict", "ask", "tofu", "none"], choices=list(get_args(HostKeyCheck)),
default="ask", default="ask",
help="Host key (.ssh/known_hosts) check mode.", help="Host key (.ssh/known_hosts) check mode.",
) )

View File

@@ -2,11 +2,13 @@ import argparse
import logging import logging
import sys import sys
from pathlib import Path from pathlib import Path
from typing import get_args
from clan_lib.errors import ClanError from clan_lib.errors import ClanError
from clan_lib.flake import require_flake from clan_lib.flake import require_flake
from clan_lib.machines.install import BuildOn, InstallOptions, run_machine_install from clan_lib.machines.install import BuildOn, InstallOptions, run_machine_install
from clan_lib.machines.machines import Machine from clan_lib.machines.machines import Machine
from clan_lib.ssh.host_key import HostKeyCheck
from clan_lib.ssh.remote import Remote from clan_lib.ssh.remote import Remote
from clan_cli.completions import ( from clan_cli.completions import (
@@ -65,6 +67,15 @@ def install_command(args: argparse.Namespace) -> None:
if ask != "y": if ask != "y":
return None return None
if args.identity_file:
target_host = target_host.override(private_key=args.identity_file)
if password:
target_host = target_host.override(password=password)
if use_tor:
target_host = target_host.override(tor_socks=True)
return run_machine_install( return run_machine_install(
InstallOptions( InstallOptions(
machine=machine, machine=machine,
@@ -72,11 +83,8 @@ def install_command(args: argparse.Namespace) -> None:
phases=args.phases, phases=args.phases,
debug=args.debug, debug=args.debug,
no_reboot=args.no_reboot, no_reboot=args.no_reboot,
build_on=BuildOn(args.build_on) if args.build_on is not None else None, build_on=args.build_on if args.build_on is not None else None,
update_hardware_config=HardwareConfig(args.update_hardware_config), update_hardware_config=HardwareConfig(args.update_hardware_config),
password=password,
identity_file=args.identity_file,
use_tor=use_tor,
), ),
target_host=target_host, target_host=target_host,
) )
@@ -99,13 +107,14 @@ def register_install_parser(parser: argparse.ArgumentParser) -> None:
) )
parser.add_argument( parser.add_argument(
"--host-key-check", "--host-key-check",
choices=["strict", "ask", "tofu", "none"], choices=list(get_args(HostKeyCheck)),
default="ask", default="ask",
help="Host key (.ssh/known_hosts) check mode.", help="Host key (.ssh/known_hosts) check mode.",
) )
parser.add_argument( parser.add_argument(
"--build-on", "--build-on",
choices=[x.value for x in BuildOn], choices=list(get_args(BuildOn)),
default=None, default=None,
help="where to build the NixOS configuration", help="where to build the NixOS configuration",
) )

View File

@@ -1,6 +1,7 @@
import argparse import argparse
import logging import logging
import sys import sys
from typing import get_args
from clan_lib.async_run import AsyncContext, AsyncOpts, AsyncRuntime from clan_lib.async_run import AsyncContext, AsyncOpts, AsyncRuntime
from clan_lib.errors import ClanError from clan_lib.errors import ClanError
@@ -12,6 +13,7 @@ from clan_lib.machines.machines import Machine
from clan_lib.machines.suggestions import validate_machine_names from clan_lib.machines.suggestions import validate_machine_names
from clan_lib.machines.update import run_machine_update from clan_lib.machines.update import run_machine_update
from clan_lib.nix import nix_config from clan_lib.nix import nix_config
from clan_lib.ssh.host_key import HostKeyCheck
from clan_lib.ssh.remote import Remote from clan_lib.ssh.remote import Remote
from clan_cli.completions import ( from clan_cli.completions import (
@@ -174,7 +176,7 @@ def register_update_parser(parser: argparse.ArgumentParser) -> None:
parser.add_argument( parser.add_argument(
"--host-key-check", "--host-key-check",
choices=["strict", "ask", "tofu", "none"], choices=list(get_args(HostKeyCheck)),
default="ask", default="ask",
help="Host key (.ssh/known_hosts) check mode.", help="Host key (.ssh/known_hosts) check mode.",
) )

View File

@@ -4,7 +4,7 @@ import logging
import textwrap import textwrap
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any, get_args
from clan_lib.cmd import run from clan_lib.cmd import run
from clan_lib.errors import ClanError from clan_lib.errors import ClanError
@@ -220,7 +220,7 @@ def register_parser(parser: argparse.ArgumentParser) -> None:
parser.add_argument( parser.add_argument(
"--host-key-check", "--host-key-check",
choices=["strict", "ask", "tofu", "none"], choices=list(get_args(HostKeyCheck)),
default="tofu", default="tofu",
help="Host key (.ssh/known_hosts) check mode.", help="Host key (.ssh/known_hosts) check mode.",
) )

View File

@@ -1,9 +1,9 @@
import logging import logging
import os import os
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from typing import Literal
from clan_cli.facts.generate import generate_facts from clan_cli.facts.generate import generate_facts
from clan_cli.machines.hardware import HardwareConfig from clan_cli.machines.hardware import HardwareConfig
@@ -18,10 +18,7 @@ from clan_lib.ssh.remote import Remote
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class BuildOn(Enum): BuildOn = Literal["auto", "local", "remote"]
AUTO = "auto"
LOCAL = "local"
REMOTE = "remote"
@dataclass @dataclass
@@ -33,9 +30,6 @@ class InstallOptions:
phases: str | None = None phases: str | None = None
build_on: BuildOn | None = None build_on: BuildOn | None = None
update_hardware_config: HardwareConfig = HardwareConfig.NONE update_hardware_config: HardwareConfig = HardwareConfig.NONE
password: str | None = None
identity_file: Path | None = None
use_tor: bool = False
@API.register @API.register
@@ -75,8 +69,8 @@ def run_machine_install(opts: InstallOptions, target_host: Remote) -> None:
machine.name, partitioning_secrets, phases=["partitioning"] machine.name, partitioning_secrets, phases=["partitioning"]
) )
if opts.password: if target_host.password:
os.environ["SSHPASS"] = opts.password os.environ["SSHPASS"] = target_host.password
cmd = [ cmd = [
"nixos-anywhere", "nixos-anywhere",
@@ -114,18 +108,18 @@ def run_machine_install(opts: InstallOptions, target_host: Remote) -> None:
] ]
) )
if opts.password: if target_host.password:
cmd += [ cmd += [
"--env-password", "--env-password",
"--ssh-option", "--ssh-option",
"IdentitiesOnly=yes", "IdentitiesOnly=yes",
] ]
if opts.identity_file: if target_host.private_key:
cmd += ["-i", str(opts.identity_file)] cmd += ["-i", str(target_host.private_key)]
if opts.build_on: if opts.build_on:
cmd += ["--build-on", opts.build_on.value] cmd += ["--build-on", opts.build_on]
if target_host.port: if target_host.port:
cmd += ["--ssh-port", str(target_host.port)] cmd += ["--ssh-port", str(target_host.port)]
@@ -139,7 +133,7 @@ def run_machine_install(opts: InstallOptions, target_host: Remote) -> None:
cmd.extend(opts.machine.flake.nix_options or []) cmd.extend(opts.machine.flake.nix_options or [])
cmd.append(target_host.target) cmd.append(target_host.target)
if opts.use_tor: if target_host.tor_socks:
# nix copy does not support tor socks proxy # nix copy does not support tor socks proxy
# cmd.append("--ssh-option") # cmd.append("--ssh-option")
# cmd.append("ProxyCommand=nc -x 127.0.0.1:9050 -X 5 %h %p") # cmd.append("ProxyCommand=nc -x 127.0.0.1:9050 -X 5 %h %p")

View File

@@ -7,7 +7,7 @@ from clan_lib.errors import ClanError
HostKeyCheck = Literal[ HostKeyCheck = Literal[
"strict", # Strictly check ssh host keys, prompt for unknown ones "strict", # Strictly check ssh host keys, prompt for unknown ones
"ask", # Ask for confirmation on first use "ask", # Ask for confirmation on first use
"tofu", # Trust on ssh keys on first use "accept-new", # Trust on ssh keys on first use
"none", # Do not check ssh host keys "none", # Do not check ssh host keys
] ]
@@ -21,7 +21,7 @@ def hostkey_to_ssh_opts(host_key_check: HostKeyCheck) -> list[str]:
return ["-o", "StrictHostKeyChecking=yes"] return ["-o", "StrictHostKeyChecking=yes"]
case "ask": case "ask":
return [] return []
case "tofu": case "accept-new" | "tofu":
return ["-o", "StrictHostKeyChecking=accept-new"] return ["-o", "StrictHostKeyChecking=accept-new"]
case "none": case "none":
return [ return [

View File

@@ -531,7 +531,7 @@ def check_machine_ssh_reachable(
f"Checking SSH reachability for {remote.target} on port {remote.port or 22}", f"Checking SSH reachability for {remote.target} on port {remote.port or 22}",
) )
address_family = socket.AF_INET6 if ":" in remote.address else socket.AF_INET address_family = socket.AF_INET6 if remote.is_ipv6() else socket.AF_INET
for _ in range(opts.retries): for _ in range(opts.retries):
with socket.socket(address_family, socket.SOCK_STREAM) as sock: with socket.socket(address_family, socket.SOCK_STREAM) as sock:
sock.settimeout(opts.timeout) sock.settimeout(opts.timeout)