Phase 2: Docker and OIDC auth
This commit is contained in:
@@ -10,6 +10,7 @@ commands are shell-quoted by callers. This is important for two reasons:
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import posixpath
|
||||
import shlex
|
||||
from dataclasses import dataclass
|
||||
@@ -17,6 +18,8 @@ from typing import Any
|
||||
|
||||
import paramiko
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class CommandResult:
|
||||
@@ -88,14 +91,25 @@ class RemoteSSHClient:
|
||||
"""
|
||||
client = self.connect()
|
||||
shell_command = f"/bin/sh -c {shlex.quote(command)}"
|
||||
logger.debug("SSH run host=%s timeout=%s command=%s", self.host, timeout or self.timeout, command)
|
||||
stdin, stdout, stderr = client.exec_command(shell_command, timeout=timeout or self.timeout)
|
||||
exit_status = stdout.channel.recv_exit_status()
|
||||
return CommandResult(
|
||||
result = CommandResult(
|
||||
command=command,
|
||||
exit_status=exit_status,
|
||||
stdout=stdout.read().decode(errors="replace"),
|
||||
stderr=stderr.read().decode(errors="replace"),
|
||||
)
|
||||
if result.exit_status == 0:
|
||||
logger.debug("SSH command ok host=%s exit_status=%s", self.host, result.exit_status)
|
||||
else:
|
||||
logger.warning(
|
||||
"SSH command failed host=%s exit_status=%s stderr=%s",
|
||||
self.host,
|
||||
result.exit_status,
|
||||
result.stderr.strip() or result.stdout.strip(),
|
||||
)
|
||||
return result
|
||||
|
||||
def list_dir(self, path: str) -> CommandResult:
|
||||
"""List one remote directory as JSON.
|
||||
@@ -123,12 +137,16 @@ class RemoteSSHClient:
|
||||
"print(json.dumps(rows))"
|
||||
)
|
||||
)
|
||||
return self.run(command)
|
||||
result = self.run(command)
|
||||
logger.info("SSH list_dir path=%s exit_status=%s", path, result.exit_status)
|
||||
return result
|
||||
|
||||
def stat_path(self, path: str) -> CommandResult:
|
||||
"""Run stat for a remote file or directory path."""
|
||||
quoted = shlex.quote(path)
|
||||
return self.run(f"stat --printf='%F\\n%s bytes\\n%y\\n%n\\n' {quoted}")
|
||||
result = self.run(f"stat --printf='%F\\n%s bytes\\n%y\\n%n\\n' {quoted}")
|
||||
logger.info("SSH stat path=%s exit_status=%s", path, result.exit_status)
|
||||
return result
|
||||
|
||||
def ffprobe_json(self, path: str) -> dict[str, Any]:
|
||||
"""Run ffprobe and parse JSON output for a remote media file."""
|
||||
@@ -137,6 +155,7 @@ class RemoteSSHClient:
|
||||
"ffprobe -v error -show_format -show_streams -print_format json " + quoted,
|
||||
timeout=60,
|
||||
)
|
||||
logger.info("SSH ffprobe path=%s exit_status=%s", path, result.exit_status)
|
||||
if result.exit_status != 0:
|
||||
raise RuntimeError(result.stderr or result.stdout or "ffprobe failed")
|
||||
return json.loads(result.stdout)
|
||||
|
||||
Reference in New Issue
Block a user