#!/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"