#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd -- "${SCRIPT_DIR}/../.." && pwd)" MODE="${MODE:-tcp}" ACTION="up" DETACH="${DETACH:-1}" BUILD="${BUILD:-1}" TARGET_PLATFORM="${TARGET_PLATFORM:-linux/arm64}" SERVER_IMAGE="${SERVER_IMAGE:-${RPKI_RTR_SERVER_IMAGE:-rpki-rtr:latest}}" BUILDX_PUSH="${BUILDX_PUSH:-0}" BUILDX_BUILDER="${BUILDX_BUILDER:-}" SKIP_BASE_IMAGE_PULL="${SKIP_BASE_IMAGE_PULL:-0}" usage() { cat <<'EOF' Usage: deploy/server/build-run.sh [action] [options] Actions: up Build and start server (default) build Build server image with docker compose buildx Build server image with docker buildx rebuild Rebuild image without cache, then start server down|stop Stop server compose stack restart Stop, then build and start server logs Follow server logs ps Show server compose status Options: --mode MODE base|tcp|tls|ssh (default: tcp) --platform VALUE Target platform for buildx (default: linux/arm64) --server-image TAG Server image tag for buildx and compose --push Push buildx image instead of loading it locally --builder NAME buildx builder name --skip-base-pull Do not pull Dockerfile base images before buildx --foreground Run server in foreground --no-build Start without --build -h|--help Show this help EOF } die() { echo "error: $*" >&2 exit 1 } has_compose() { docker compose version >/dev/null 2>&1 } has_buildx() { docker buildx version >/dev/null 2>&1 } pull_base_images() { [[ "${SKIP_BASE_IMAGE_PULL}" == "1" ]] && return local platform image local -a platforms images IFS=',' read -r -a platforms <<< "${TARGET_PLATFORM}" images=(rust:1.89-bookworm debian:bookworm-slim) for platform in "${platforms[@]}"; do for image in "${images[@]}"; do docker pull --platform "${platform}" "${image}" done done } compose_files() { case "${MODE}" in base) printf '%s\n' -f "${SCRIPT_DIR}/docker-compose.yml" ;; tcp) printf '%s\n' -f "${SCRIPT_DIR}/docker-compose.tcp.yml" ;; tls) printf '%s\n' -f "${SCRIPT_DIR}/docker-compose.tls.yml" ;; ssh) printf '%s\n' -f "${SCRIPT_DIR}/docker-compose.ssh.yml" ;; *) die "unsupported mode '${MODE}', expected base|tcp|tls|ssh" ;; esac } compose_args() { local -a args files args=() [[ -f "${SCRIPT_DIR}/.env" ]] && args+=(--env-file "${SCRIPT_DIR}/.env") mapfile -t files < <(compose_files) args+=("${files[@]}") printf '%s\n' "${args[@]}" } compose_run() { local -a args mapfile -t args < <(compose_args) docker compose "${args[@]}" "$@" } up_server() { local -a up_args up_args=(up) [[ "${DETACH}" == "1" ]] && up_args+=(-d) [[ "${BUILD}" == "1" ]] && up_args+=(--build) compose_run "${up_args[@]}" } buildx_server() { has_buildx || die "docker buildx is not available" if [[ "${TARGET_PLATFORM}" == *,* && "${BUILDX_PUSH}" != "1" ]]; then die "multi-platform buildx output requires --push" fi local -a output_args builder_args [[ "${BUILDX_PUSH}" == "1" ]] && output_args=(--push) || output_args=(--load) [[ -n "${BUILDX_BUILDER}" ]] && builder_args=(--builder "${BUILDX_BUILDER}") || builder_args=() pull_base_images docker buildx build "${builder_args[@]}" \ --platform "${TARGET_PLATFORM}" \ -f "${SCRIPT_DIR}/Dockerfile" \ -t "${SERVER_IMAGE}" \ "${output_args[@]}" \ "${REPO_ROOT}" } while [[ $# -gt 0 ]]; do case "$1" in up|build|buildx|rebuild|down|stop|restart|logs|ps) ACTION="$1" shift ;; --mode) [[ $# -ge 2 ]] || die "--mode requires a value" MODE="$2" shift 2 ;; --platform) [[ $# -ge 2 ]] || die "--platform requires a value" TARGET_PLATFORM="$2" shift 2 ;; --server-image) [[ $# -ge 2 ]] || die "--server-image requires a value" SERVER_IMAGE="$2" shift 2 ;; --push) BUILDX_PUSH=1 shift ;; --builder) [[ $# -ge 2 ]] || die "--builder requires a value" BUILDX_BUILDER="$2" shift 2 ;; --skip-base-pull) SKIP_BASE_IMAGE_PULL=1 shift ;; --foreground) DETACH=0 shift ;; --no-build) BUILD=0 shift ;; -h|--help) usage exit 0 ;; *) die "unknown argument '$1'" ;; esac done export RPKI_RTR_SERVER_IMAGE="${SERVER_IMAGE}" if [[ "${ACTION}" == "buildx" ]]; then has_buildx || die "docker buildx is not available" else has_compose || die "docker compose is not available" fi cd "${REPO_ROOT}" case "${ACTION}" in up) up_server ;; build) compose_run build ;; buildx) buildx_server ;; rebuild) compose_run build --no-cache up_server ;; down|stop) compose_run down ;; restart) compose_run down up_server ;; logs) compose_run logs -f ;; ps) compose_run ps ;; esac