Files
clan-core/pkgs/clan-cli/clan_lib/ssh/host.py
DavHau c33fd4e504 ssh: Introduce LocalHost vs. Remote via Host interface
Motivation: local builds and deployments without ssh

Add a new interface `Host` which is implemented bei either `Remote` or `Localhost`

This simplifies all interactions with hosts. THe caller does ot need to know if the Host is remote or local in mot cases anymore
2025-08-05 13:16:59 +02:00

86 lines
2.3 KiB
Python

"""Base Host interface for both local and remote command execution."""
import logging
from abc import ABC, abstractmethod
from collections.abc import Iterator
from contextlib import contextmanager
from dataclasses import dataclass
from clan_lib.cmd import CmdOut, RunOpts
cmdlog = logging.getLogger(__name__)
@dataclass(frozen=True)
class Host(ABC):
"""
Abstract base class for host command execution.
This provides a common interface for both local and remote hosts.
"""
command_prefix: str
@property
@abstractmethod
def target(self) -> str:
"""Return a descriptive target string for this host."""
@property
@abstractmethod
def user(self) -> str:
"""Return the user for this host."""
@abstractmethod
def run(
self,
cmd: list[str],
opts: RunOpts | None = None,
extra_env: dict[str, str] | None = None,
tty: bool = False,
verbose_ssh: bool = False,
quiet: bool = False,
control_master: bool = True,
) -> CmdOut:
"""
Run a command on the host.
Args:
cmd: Command to execute
opts: Run options
extra_env: Additional environment variables
tty: Whether to allocate a TTY (for remote hosts)
verbose_ssh: Enable verbose SSH output (for remote hosts)
quiet: Suppress command logging
control_master: Use SSH ControlMaster (for remote hosts)
Returns:
Command output
"""
@contextmanager
@abstractmethod
def become_root(self) -> Iterator["Host"]:
"""
Context manager to execute commands as root.
"""
@contextmanager
@abstractmethod
def host_connection(self) -> Iterator["Host"]:
"""
Context manager to manage host connections.
For remote hosts, this manages SSH ControlMaster connections.
For local hosts, this is a no-op that returns self.
"""
@abstractmethod
def nix_ssh_env(
self,
env: dict[str, str] | None = None,
control_master: bool = True,
) -> dict[str, str]:
"""
Get environment variables for Nix operations.
Remote hosts will add NIX_SSHOPTS, local hosts won't.
"""