Merge pull request 'Host: actual setup ssh controlmaster' (#3515) from fix-control-master into main

Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/3515
This commit is contained in:
Mic92
2025-05-06 11:21:05 +00:00
3 changed files with 27 additions and 18 deletions

View File

@@ -149,13 +149,14 @@ class Machine:
@contextmanager @contextmanager
def target_host(self) -> Iterator[Host]: def target_host(self) -> Iterator[Host]:
yield parse_deployment_address( with parse_deployment_address(
self.name, self.name,
self.target_host_address, self.target_host_address,
self.host_key_check, self.host_key_check,
private_key=self.private_key, private_key=self.private_key,
meta={"machine": self}, meta={"machine": self},
) ) as target_host:
yield target_host
@contextmanager @contextmanager
def build_host(self) -> Iterator[Host | None]: def build_host(self) -> Iterator[Host | None]:
@@ -168,14 +169,15 @@ class Machine:
yield None yield None
return return
# enable ssh agent forwarding to allow the build host to access the target host # enable ssh agent forwarding to allow the build host to access the target host
yield parse_deployment_address( with parse_deployment_address(
self.name, self.name,
build_host, build_host,
self.host_key_check, self.host_key_check,
forward_agent=True, forward_agent=True,
private_key=self.private_key, private_key=self.private_key,
meta={"machine": self}, meta={"machine": self},
) ) as build_host:
yield build_host
@cached_property @cached_property
def deploy_as_root(self) -> bool: def deploy_as_root(self) -> bool:

View File

@@ -179,11 +179,11 @@ def deploy_machine(machine: Machine) -> None:
switch_cmd = [f"{machine._class_}-rebuild", "switch", *nix_options] switch_cmd = [f"{machine._class_}-rebuild", "switch", *nix_options]
test_cmd = [f"{machine._class_}-rebuild", "test", *nix_options] test_cmd = [f"{machine._class_}-rebuild", "test", *nix_options]
env = host.nix_ssh_env(None) remote_env = host.nix_ssh_env(None, local_ssh=False)
ret = host.run( ret = host.run(
switch_cmd, switch_cmd,
RunOpts(check=False, msg_color=MsgColor(stderr=AnsiColor.DEFAULT)), RunOpts(check=False, msg_color=MsgColor(stderr=AnsiColor.DEFAULT)),
extra_env=env, extra_env=remote_env,
become_root=become_root, become_root=become_root,
) )
@@ -209,7 +209,7 @@ def deploy_machine(machine: Machine) -> None:
msg_color=MsgColor(stderr=AnsiColor.DEFAULT), msg_color=MsgColor(stderr=AnsiColor.DEFAULT),
needs_user_terminal=True, needs_user_terminal=True,
), ),
extra_env=env, extra_env=remote_env,
become_root=become_root, become_root=become_root,
) )

View File

@@ -42,14 +42,9 @@ class Host:
_temp_dir: TemporaryDirectory | None = None _temp_dir: TemporaryDirectory | None = None
def setup_control_master(self, control_path: Path) -> None: def __enter__(self) -> "Host":
self.ssh_options["ControlMaster"] = "auto"
self.ssh_options["ControlPath"] = str(control_path / "clan-%h-%p-%r")
self.ssh_options["ControlPersist"] = "30m"
def __enter__(self) -> None:
self._temp_dir = TemporaryDirectory(prefix="clan-ssh-") self._temp_dir = TemporaryDirectory(prefix="clan-ssh-")
self.setup_control_master(Path(self._temp_dir.name)) return self
def __exit__( def __exit__(
self, self,
@@ -187,15 +182,17 @@ class Host:
# Run the ssh command # Run the ssh command
return run(ssh_cmd, opts) return run(ssh_cmd, opts)
def nix_ssh_env(self, env: dict[str, str] | None) -> dict[str, str]: def nix_ssh_env(
self, env: dict[str, str] | None, local_ssh: bool = True
) -> dict[str, str]:
if env is None: if env is None:
env = {} env = {}
env["NIX_SSHOPTS"] = " ".join(self.ssh_cmd_opts) env["NIX_SSHOPTS"] = " ".join(self.ssh_cmd_opts(local_ssh=local_ssh))
return env return env
@property
def ssh_cmd_opts( def ssh_cmd_opts(
self, self,
local_ssh: bool = True,
) -> list[str]: ) -> list[str]:
ssh_opts = ["-A"] if self.forward_agent else [] ssh_opts = ["-A"] if self.forward_agent else []
if self.port: if self.port:
@@ -209,6 +206,16 @@ class Host:
if self.private_key: if self.private_key:
ssh_opts.extend(["-i", str(self.private_key)]) ssh_opts.extend(["-i", str(self.private_key)])
if local_ssh and self._temp_dir:
ssh_opts.extend(["-o", "ControlPersist=30m"])
ssh_opts.extend(
[
"-o",
f"ControlPath={Path(self._temp_dir.name) / 'clan-%h-%p-%r'}",
]
)
ssh_opts.extend(["-o", "ControlMaster=auto"])
return ssh_opts return ssh_opts
def ssh_cmd( def ssh_cmd(
@@ -226,7 +233,7 @@ class Host:
self.password, self.password,
] ]
ssh_opts = self.ssh_cmd_opts ssh_opts = self.ssh_cmd_opts()
if verbose_ssh or self.verbose_ssh: if verbose_ssh or self.verbose_ssh:
ssh_opts.extend(["-v"]) ssh_opts.extend(["-v"])
if tty: if tty: