diff --git a/tests/netns/ci.sh b/tests/netns/ci.sh new file mode 100644 index 0000000..561ca39 --- /dev/null +++ b/tests/netns/ci.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# ci.sh — точка входа netns-стенда для CI (#17), минуя make (issue #56). +# +# Зачем отдельный скрипт: GNU make возвращает 2 на любой упавший рецепт, поэтому +# контракт кодов run.sh (0 — тесты прошли, 1 — тесты упали, 3 — нет прав на netns, +# 4 — сломан стенд) через `make test-netns-ci` до пайплайна не доходит. ci.sh +# запускает контейнер напрямую и возвращает его код как есть — workflow отличает +# «раннеру не дали CAP_NET_ADMIN» от «регрессия стенда». +# +# tests/netns/ci.sh # прогон стенда, код 0/1/3/4 +# tests/netns/ci.sh --no-tests # только поднять топологию +# tests/netns/ci.sh --keep-topology # оставить топологию для отладки +# AWG_NS_PRIVILEGED=0 tests/netns/ci.sh # ветка «нет прав» → ожидаем код 3 +# +# Переменные окружения: +# AWG_DASH_IMAGE образ с харнессом (по умолчанию awg-dash:dev) +# AWG_NS_EXEC_MODE auto|ip|nsenter — вход в netns (issue #56) +# NETNS_EXEC_MODE make-алиас того же (Makefile прокидывает его сюда) +# AWG_NS_PRIVILEGED 1 (по умолчанию) — как в compose; 0 — прогон без +# privileged (проверка ветки «код 3») +# AWG_DASH_TEST_ARTIFACTS каталог артефактов (по умолчанию /artifacts) +# COMPOSE_PROJECT_NAME имя compose-проекта (по умолчанию awg-dash-test) +# +# Коды возврата: 0/1/3/4 — как у run.sh; 2 — ошибка вызова или окружения +# (нет docker/compose/образа, неизвестный аргумент). Ветку 2 CI не должен +# путать с 3: 2 — сломан сам пайплайн/раннер, 3 — раннер без прав на netns. +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +COMPOSE_FILE="$ROOT/deploy/docker-compose.test.yml" +IMAGE="${AWG_DASH_IMAGE:-awg-dash:dev}" +ARTIFACTS="${AWG_DASH_TEST_ARTIFACTS:-$ROOT/artifacts}" +export COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-awg-dash-test}" + +# AWG_NS_EXEC_MODE — документированное имя, NETNS_EXEC_MODE — алиас из Makefile. +NS_MODE="${AWG_NS_EXEC_MODE:-${NETNS_EXEC_MODE:-auto}}" +case "$NS_MODE" in + auto|ip|nsenter) ;; + *) + echo "[ci][fail] AWG_NS_EXEC_MODE='${NS_MODE}' — ожидается auto|ip|nsenter" >&2 + exit 2 + ;; +esac +export AWG_NS_EXEC_MODE="$NS_MODE" + +die() { echo "[ci][fail] $*" >&2; exit 2; } + +[ -f "$COMPOSE_FILE" ] || die "нет ${COMPOSE_FILE} (задача #21)" +command -v docker >/dev/null 2>&1 || die "нет docker в PATH — CI-раннер должен быть с docker (#17)" +docker compose version >/dev/null 2>&1 || die "нет docker compose v2 в PATH (#17)" +docker image inspect "$IMAGE" >/dev/null 2>&1 \ + || die "нет образа ${IMAGE}: соберите его (make build после мержа #18) или задайте AWG_DASH_IMAGE" + +mkdir -p "$ARTIFACTS" +export AWG_DASH_TEST_ARTIFACTS="$ARTIFACTS" +export AWG_DASH_IMAGE="$IMAGE" + +COMPOSE_ARGS=(-f "$COMPOSE_FILE") +CLEANUP_DIR="" +PRIVILEGED="${AWG_NS_PRIVILEGED:-1}" +if [ "$PRIVILEGED" = "0" ]; then + # Оверрайд снимает privileged у сервиса netns — проверка ветки «код 3». + CLEANUP_DIR="$(mktemp -d -t awg-ci-XXXXXX)" + printf 'services:\n netns:\n privileged: false\n' >"${CLEANUP_DIR}/no-privileged.yml" + COMPOSE_ARGS+=(-f "${CLEANUP_DIR}/no-privileged.yml") + # shellcheck disable=SC2064 # раскрытие $CLEANUP_DIR нужно на момент trap + trap "rm -rf '${CLEANUP_DIR}'" EXIT +fi + +echo "[ci] образ=${IMAGE} режим netns=${NS_MODE} privileged=${PRIVILEGED} артефакты=${ARTIFACTS}" + +rc=0 +docker compose "${COMPOSE_ARGS[@]}" run --rm -T netns "$@" || rc=$? + +case "$rc" in + 0) echo "[ci][ ok ] netns-стенд: тесты прошли (код 0)" ;; + 1) echo "[ci][fail] netns-стенд: тесты упали (код 1)" >&2 ;; + 3) echo "[ci][blocked] netns-стенд: окружение не даёт прав на netns (код 3) — раннеру нужен --privileged (#17)" >&2 ;; + 4) echo "[ci][fail] netns-стенд: сломан стенд (код 4) — см. лог выше" >&2 ;; + *) echo "[ci][fail] netns-стенд: неожиданный код ${rc}" >&2 ;; +esac +exit "$rc"