Commit b77649b2 authored by Ivan Mazhukin's avatar Ivan Mazhukin

add multisystem tests

parent 2edfc216
......@@ -25,6 +25,20 @@
./epm-docker-test.sh ayugram fedora
```
Запуск одного приложения сразу на нескольких системах:
```bash
./epm-docker-test.sh ayugram fedora debian ubuntu
```
Запуск по пресету:
```bash
./epm-docker-test.sh ayugram --preset mainstream
./epm-docker-test.sh ayugram --preset russian
./epm-docker-test.sh ayugram --preset all
```
Запуск с явным путём к `eepm`:
```bash
......@@ -46,8 +60,9 @@
## Формат команды
```bash
./epm-docker-test.sh [options] play <app> <system>
./epm-docker-test.sh [options] <app> <system>
./epm-docker-test.sh [options] play <app> <system> [<system> ...]
./epm-docker-test.sh [options] <app> <system> [<system> ...]
./epm-docker-test.sh [options] <app> --preset <name>
```
Сейчас поддерживается только команда `play`.
......@@ -57,6 +72,13 @@
- `<app>`: имя приложения для `epm play`
- `<system>`: целевая система или docker image
Можно передать:
- одну систему;
- несколько систем через пробел;
- один или несколько `--preset`;
- комбинацию явных систем и `--preset`.
Примеры нормализации `<system>`:
- `fedora` -> `fedora:latest`
......@@ -74,8 +96,19 @@
- `--eepm-dir <path>`: явный путь к каталогу `eepm`
- `--builder-user <user>`: пользователь для builder64-источника
- `--builder-path <path>`: явный builder64-путь вместо дефолта
- `--preset <all|russian|mainstream>`: добавить набор систем
- `--log-root <path>`: каталог для логов
## Пресеты
Сейчас определены такие пресеты:
- `mainstream`: `fedora`, `debian`, `ubuntu`, `opensuse/leap`, `alpine`, `void`
- `russian`: `alt:sisyphus`
- `all`: `mainstream` + `russian`
Повторы автоматически удаляются после нормализации имени системы.
## Источник eepm
### local
......@@ -169,8 +202,10 @@ Runner: local docker (inside ssh session, user: builder-robot)
Сейчас bootstrap завязан на семейство системы:
- `altlinux|alt`: `repo set etersoft`, `update`, затем установка `wget glibc-pthread file`
- `debian|ubuntu`: `update`, затем установка `bash wget ca-certificates coreutils file`
- все остальные: установка `wget file bash`
- `debian|ubuntu`: `update`, затем установка `wget ca-certificates coreutils file`
- `alpine`: `apk update`, `apk add bash`, затем `update` и установка `wget file`
- `void`: `xbps-install -Syu`, `xbps-install -Sy bash`, затем `update` и установка `wget file`
- все остальные: `update`, затем установка `wget file bash`
## Логи
......@@ -202,6 +237,8 @@ ayugram-fedora-latest-20260331-185448.log
При ошибке скрипт дополнительно выводит короткую выборку критичных строк из лога.
Если систем было несколько, в конце дополнительно печатается сводная таблица `PASS/FAIL` со ссылкой на лог каждого прогона.
## Типовые сценарии
Запустить локально, если Docker доступен:
......
......@@ -10,7 +10,6 @@ DEFAULT_LOG_ROOT="${XDG_STATE_HOME:-$HOME/.local/state}/epm-docker-test"
COMMAND="play"
APP_NAME=""
SYSTEM_INPUT=""
SYSTEM_IMAGE=""
RUN_MODE="auto"
REMOTE_HOST="$DEFAULT_REMOTE_HOST"
......@@ -25,22 +24,19 @@ LOG_FILE=""
REMOTE_ARGS=()
REMOTE_SYNC_DIR=""
RUN_TOKEN="$(date +%Y%m%d-%H%M%S)-$$"
# TODO(next):
# - support one app across multiple systems in a single run:
# epm-docker-test.sh ayugram fedora:43 debian ubuntu alt:sisyphus
# - support system presets such as:
# --preset all
# --preset russian
# --preset mainstream
# - print a final summary table with PASS/FAIL per system
# - keep multi-app runs out of scope for now; parallel shell is enough there
SYSTEM_INPUTS=()
PRESET_NAMES=()
TARGET_SYSTEMS=()
SUMMARY_SYSTEMS=()
SUMMARY_RESULTS=()
SUMMARY_LOGS=()
COLOR_RESET=""
COLOR_WARN=""
COLOR_ERROR=""
COLOR_LOG_FATAL=""
COLOR_LOG_ERROR=""
COLOR_PASS=""
SSH_BASE_ARGS=(
-o BatchMode=yes
-o StrictHostKeyChecking=accept-new
......@@ -52,13 +48,15 @@ if [[ -t 2 && -z "${NO_COLOR:-}" ]]; then
COLOR_ERROR=$'\033[31m'
COLOR_LOG_FATAL=$'\033[31m'
COLOR_LOG_ERROR=$'\033[33m'
COLOR_PASS=$'\033[32m'
fi
usage() {
cat <<'EOF'
Usage:
epm-docker-test.sh [options] play <app> <system>
epm-docker-test.sh [options] <app> <system>
epm-docker-test.sh [options] play <app> <system> [<system> ...]
epm-docker-test.sh [options] <app> <system> [<system> ...]
epm-docker-test.sh [options] <app> --preset <name>
Options:
--mode <auto|local|remote> Runner mode. Default: auto
......@@ -71,11 +69,15 @@ Options:
--eepm-dir <path> Explicit eepm tree path
--builder-user <user> Preferred owner for ~/Projects/... lookup
--builder-path <path> Explicit builder64 source path
--preset <all|russian|mainstream>
Add a named system preset
--log-root <path> Directory for saved logs
-h, --help Show this help
Examples:
epm-docker-test.sh nginx fedora
epm-docker-test.sh nginx fedora debian ubuntu
epm-docker-test.sh nginx --preset mainstream
epm-docker-test.sh play nginx Fedora/43
epm-docker-test.sh --remote-user builder-robot --eepm-source builder64 nginx fedora:43
EOF
......@@ -132,7 +134,7 @@ normalize_system_name() {
image_tag="${normalized##*/}"
case "$image_tag" in
[0-9]*|v[0-9]*)
[0-9]*|v[0-9]*|p[0-9]*)
normalized="${image_name}:${image_tag}"
;;
*)
......@@ -154,6 +156,61 @@ normalize_system_name() {
esac
}
expand_preset_systems() {
case "$1" in
mainstream)
printf '%s\n' \
fedora \
debian \
ubuntu \
opensuse/leap \
alpine \
void
;;
russian)
printf '%s\n' \
alt:sisyphus
;;
all)
expand_preset_systems mainstream
expand_preset_systems russian
;;
*)
fatal "Unknown preset: $1"
;;
esac
}
build_target_systems() {
local raw_system
local normalized_system
local preset_name
local seen="|"
TARGET_SYSTEMS=()
for preset_name in "${PRESET_NAMES[@]}"; do
while IFS= read -r raw_system; do
[[ -n "$raw_system" ]] || continue
normalized_system="$(normalize_system_name "$raw_system")"
if [[ "$seen" != *"|$normalized_system|"* ]]; then
TARGET_SYSTEMS+=("$normalized_system")
seen="${seen}${normalized_system}|"
fi
done < <(expand_preset_systems "$preset_name")
done
for raw_system in "${SYSTEM_INPUTS[@]}"; do
normalized_system="$(normalize_system_name "$raw_system")"
if [[ "$seen" != *"|$normalized_system|"* ]]; then
TARGET_SYSTEMS+=("$normalized_system")
seen="${seen}${normalized_system}|"
fi
done
((${#TARGET_SYSTEMS[@]} > 0)) || fatal "No target systems were provided"
}
can_use_local_docker() {
command -v docker >/dev/null 2>&1 || return 1
docker info >/dev/null 2>&1
......@@ -319,19 +376,63 @@ EOF
}
create_log_file() {
local safe_app safe_system timestamp log_name
local safe_app safe_system timestamp log_name log_root
if ! mkdir -p "$LOG_ROOT" 2>/dev/null; then
log_root="$LOG_ROOT"
if ! mkdir -p "$log_root" 2>/dev/null || [[ ! -w "$log_root" ]]; then
warn "Cannot write to log root $LOG_ROOT; falling back to ${TMPDIR:-/tmp}/epm-docker-test"
LOG_ROOT="${TMPDIR:-/tmp}/epm-docker-test"
mkdir -p "$LOG_ROOT" || fatal "Could not create log root: $LOG_ROOT"
log_root="${TMPDIR:-/tmp}/epm-docker-test"
mkdir -p "$log_root" || fatal "Could not create log root: $log_root"
[[ -w "$log_root" ]] || fatal "Could not write to log root: $log_root"
fi
safe_app="$(sanitize_name "$APP_NAME")"
safe_system="$(sanitize_name "$SYSTEM_IMAGE")"
timestamp="$(date +%Y%m%d-%H%M%S)"
log_name="${safe_app}-${safe_system}-${timestamp}.log"
printf '%s\n' "$LOG_ROOT/$log_name"
printf '%s\n' "$log_root/$log_name"
}
record_summary_result() {
SUMMARY_SYSTEMS+=("$1")
SUMMARY_RESULTS+=("$2")
SUMMARY_LOGS+=("$3")
}
print_summary_table() {
local i
local system_width=6
local result_label
local result_output
((${#SUMMARY_SYSTEMS[@]} > 1)) || return 0
for i in "${!SUMMARY_SYSTEMS[@]}"; do
if ((${#SUMMARY_SYSTEMS[i]} > system_width)); then
system_width=${#SUMMARY_SYSTEMS[i]}
fi
done
printf '\nSummary:\n'
printf '%-*s %-6s %s\n' "$system_width" "System" "Result" "Log"
printf '%-*s %-6s %s\n' "$system_width" "$(printf '%*s' "$system_width" '' | tr ' ' '-')" "------" "---"
for i in "${!SUMMARY_SYSTEMS[@]}"; do
if ((SUMMARY_RESULTS[i] == 0)); then
result_label="PASS"
result_output="${COLOR_PASS}${result_label}${COLOR_RESET}"
else
result_label="FAIL"
result_output="${COLOR_ERROR}${result_label}${COLOR_RESET}"
fi
printf '%-*s %s %s\n' \
"$system_width" \
"${SUMMARY_SYSTEMS[i]}" \
"$result_output" \
"${SUMMARY_LOGS[i]}"
done
}
print_failure_excerpt() {
......@@ -571,6 +672,41 @@ run_once() {
esac
}
run_for_system() {
local system_image="$1"
local index="$2"
local total="$3"
local status
SYSTEM_IMAGE="$system_image"
LOG_FILE="$(create_log_file)"
if ((total > 1)); then
info "Run [$index/$total]: epm $COMMAND $APP_NAME on $SYSTEM_IMAGE"
fi
info "Log file: $LOG_FILE"
info "Normalized system: $SYSTEM_IMAGE"
info "Test command: epm $COMMAND $APP_NAME"
if run_once 2>&1 | tee "$LOG_FILE"; then
status=0
else
status=$?
fi
if ((status == 0)); then
printf '\nTest passed: epm %s %s on %s\n' "$COMMAND" "$APP_NAME" "$SYSTEM_IMAGE"
printf 'Log: %s\n' "$LOG_FILE"
else
printf '\nTest failed: epm %s %s on %s\n' "$COMMAND" "$APP_NAME" "$SYSTEM_IMAGE" >&2
printf 'Log: %s\n' "$LOG_FILE" >&2
print_failure_excerpt
fi
record_summary_result "$SYSTEM_IMAGE" "$status" "$LOG_FILE"
return "$status"
}
parse_args() {
local positional=()
......@@ -623,6 +759,11 @@ parse_args() {
BUILDER_PATH="$2"
shift 2
;;
--preset)
[[ $# -ge 2 ]] || fatal "--preset requires a value"
PRESET_NAMES+=("$2")
shift 2
;;
--log-root)
[[ $# -ge 2 ]] || fatal "--log-root requires a value"
LOG_ROOT="$2"
......@@ -649,49 +790,56 @@ parse_args() {
*)
positional+=("$1")
shift
;;
;;
esac
done
if ((${#positional[@]} != 2)); then
if ((${#positional[@]} < 1)); then
usage >&2
exit 1
fi
APP_NAME="${positional[0]}"
SYSTEM_INPUT="${positional[1]}"
SYSTEM_INPUTS=("${positional[@]:1}")
if ((${#SYSTEM_INPUTS[@]} == 0 && ${#PRESET_NAMES[@]} == 0)); then
fatal "At least one target system or --preset is required"
fi
}
main() {
local status
local system_image
local status=0
local run_status
local total
local index=0
parse_args "$@"
SYSTEM_IMAGE="$(normalize_system_name "$SYSTEM_INPUT")"
if ((INTERNAL_LOCAL_RUN)); then
build_target_systems
if ((${#TARGET_SYSTEMS[@]} != 1)); then
fatal "Internal local run expects exactly one target system"
fi
SYSTEM_IMAGE="${TARGET_SYSTEMS[0]}"
run_once
return 0
fi
LOG_FILE="$(create_log_file)"
info "Log file: $LOG_FILE"
info "Normalized system: $SYSTEM_IMAGE"
info "Test command: epm $COMMAND $APP_NAME"
build_target_systems
total="${#TARGET_SYSTEMS[@]}"
if run_once 2>&1 | tee "$LOG_FILE"; then
status=0
else
status=$?
fi
for system_image in "${TARGET_SYSTEMS[@]}"; do
((index += 1))
if run_for_system "$system_image" "$index" "$total"; then
run_status=0
else
run_status=$?
status="$run_status"
fi
done
if ((status == 0)); then
printf '\nTest passed: epm %s %s on %s\n' "$COMMAND" "$APP_NAME" "$SYSTEM_IMAGE"
printf 'Log: %s\n' "$LOG_FILE"
else
printf '\nTest failed: epm %s %s on %s\n' "$COMMAND" "$APP_NAME" "$SYSTEM_IMAGE" >&2
printf 'Log: %s\n' "$LOG_FILE" >&2
print_failure_excerpt
fi
print_summary_table
return "$status"
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment