1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
|
# Copyright © The Debusine Developers
# See the AUTHORS file at the top-level directory of this distribution
#
# This file is part of Debusine. It is subject to the license terms
# in the LICENSE file found in the top-level directory of this
# distribution. No part of Debusine, including this file, may be copied,
# modified, propagated, or distributed except according to the terms
# contained in the LICENSE file.
"""Common utility methods."""
import logging
import socket
import subprocess
from functools import cached_property
from typing import Any
import yaml
logger = logging.getLogger(__name__)
class Configuration:
"""Configuration setup for the integration tests."""
DEBUSINE_SERVER_USER = 'debusine-server'
@classmethod
def get_base_url(cls) -> str:
"""Return the base URL for the test server."""
return f"https://{socket.getfqdn()}"
class RunResult:
"""Encapsulate the result of executing a command."""
def __init__(
self, stdout: str, stderr: str, returncode: int, cmd: list[str]
) -> None:
"""Initialize RunResult."""
self.stdout = stdout
self.stderr = stderr
self.returncode = returncode
self.cmd = cmd
@cached_property
def parsed_stdout(self) -> Any:
"""Return the command stdout parsed as YAML."""
# Parse it only once and only if trying to use __getitem__.
# Some cmd run by RunResult might not return valid Yaml
# (so the yaml should only be parsed if it's used)
try:
return yaml.safe_load(self.stdout)
except yaml.YAMLError as exc:
raise ValueError(
f'Error parsing\n'
f"{self.cmd=}\n"
f"{self.stdout=}\n"
f"{self.stderr=}\n"
f"{self.returncode=}\n"
f"{exc=}"
)
def __getitem__(self, key: str) -> Any:
"""Return item from parsed stdout from the command."""
if self.parsed_stdout is None:
raise KeyError(
f'KeyError: "{key}" cannot be found: stdout is None\n'
f"{self.cmd=}\n"
f"{self.stdout=}\n"
f"{self.stderr=}\n"
f"{self.returncode=}"
)
elif key not in self.parsed_stdout:
raise KeyError(
f'KeyError: "{key}" does not exist.\n'
f"{self.cmd=}\n"
f"{self.stdout=}\n"
f"{self.parsed_stdout=}\n"
f"{self.stderr=}\n"
f"{self.returncode=}"
)
return self.parsed_stdout[key]
def run_as(user: str, cmd: list[str], *, stdin: str | None = None) -> RunResult:
"""Run cmd as user using sudo."""
cmd = ['sudo', '-u', user] + cmd
return run(cmd, stdin=stdin)
def run(
cmd: list[str], *, stdin: str | None = None, timeout: float | None = None
) -> RunResult:
"""Run cmd with stdin, return subprocess.CompletedProcess."""
logger.debug('Exec: %s (stdin: %s)', cmd, stdin)
completed_process = subprocess.run(
cmd,
text=True,
capture_output=True,
input=stdin,
timeout=timeout,
)
return RunResult(
completed_process.stdout,
completed_process.stderr,
completed_process.returncode,
cmd,
)
|