railiance-platform/scripts/remote_exec.py

44 lines
1.4 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
"""Small, value-safe SSH argv transport shared by attended procedures."""
from __future__ import annotations
import shlex
import subprocess
from typing import Callable
class RemoteExecutionError(RuntimeError):
pass
def remote_command(argv: list[str]) -> str:
if not argv or any(not isinstance(part, str) or "\0" in part for part in argv):
raise RemoteExecutionError("remote argv must contain non-NUL strings")
return shlex.join(argv)
def run_remote(
host: str,
argv: list[str],
*,
label: str,
input_text: str | None = None,
allow_missing: bool = False,
runner: Callable[..., subprocess.CompletedProcess[str]] = subprocess.run,
) -> subprocess.CompletedProcess[str]:
if not host or any(character.isspace() for character in host):
raise RemoteExecutionError("remote host must be a single SSH destination")
completed = runner(
["ssh", "-o", "BatchMode=yes", host, remote_command(argv)],
text=True,
capture_output=True,
input=input_text,
check=False,
)
if completed.returncode and not (allow_missing and completed.returncode == 1):
# Remote output may contain provider or application material. Keep the
# durable error value-safe and let an attended operator inspect locally.
raise RemoteExecutionError(f"{label} failed (exit {completed.returncode})")
return completed