mirror of
https://gitea.com/gitea/act_runner
synced 2026-09-21 19:37:07 +02:00
Cache v2 makes the cache server the `ACTIONS_RESULTS_URL` origin, so artifact calls arrived there and were proxied on to Gitea, failing whenever it could not reach the instance. - Artifact calls are answered with a redirect, so the cache server opens no connection to Gitea. A scheme change or an untrusted instance is still proxied, but there the cache server is the runner itself, which already reaches Gitea. - Failures answer in twirp, not an empty `502` that clients report as `Unexpected end of JSON input`. - `cache.v2: false` really points artifacts at Gitea now. - Cache reservations are bound to the job that made them, so two jobs saving one key cannot commit against each other's upload, and a retry after a lost answer no longer fails a saved entry. - The toolkit patch, which edits the GitHub-host check out of an action's bundle, was left in the shared checkout where a job running with `runner.patch_actions: false` could inherit it. It is put back after the job's copy. - `exec` names an origin for the cache v2 it advertises, and masks its runtime token. Behaviour changes: `no_proxy` no longer exempts `cache.external_server`, and `cache.enabled: false` also stops external registration. Fixes https://gitea.com/gitea/runner/issues/1208 Fixes https://gitea.com/gitea/runner/issues/1211 Assisted by Claude (Opus 5). Reviewed-on: https://gitea.com/gitea/runner/pulls/1216 Reviewed-by: bircni <bircni@icloud.com> Co-authored-by: silverwind <me@silverwind.io>
144 lines
5.9 KiB
Bash
Executable File
144 lines
5.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Generic docker-in-docker test harness.
|
|
#
|
|
# Builds a dind image variant from the repo Dockerfile, starts its docker daemon over a
|
|
# local TCP port, and runs a Go test command against that daemon via DOCKER_HOST. This
|
|
# validates the actual docker version and behaviour shipped in the dind image, so any
|
|
# daemon-level regression surfaces here (e.g. the "docker cp" break in gitea/runner#981).
|
|
# It is deliberately generic: point it at any package/test to exercise the dind daemon.
|
|
#
|
|
# Usage: scripts/test-dind.sh [target] [-- go-test-args...]
|
|
# target: dind (default) or dind-rootless
|
|
# go-test-args: passed verbatim to `go test`. Defaults cover image env extraction,
|
|
# symlink copying and a mounted Docker job using cached images.
|
|
#
|
|
# Env:
|
|
# DIND_TEST_PORT host port for the daemon (default 32375)
|
|
# DIND_TEST_IMAGE skip the build and use this prebuilt image instead
|
|
# DIND_TEST_PRELOAD space-separated images to copy from the host daemon into the fresh one
|
|
set -euo pipefail
|
|
|
|
target="dind"
|
|
case "${1:-}" in
|
|
dind|dind-rootless) target="$1"; shift ;;
|
|
esac
|
|
[ "${1:-}" = "--" ] && shift
|
|
default_tests=false
|
|
if [ $# -eq 0 ]; then
|
|
default_tests=true
|
|
set -- -count=1 -race -run '^TestDocker$' ./act/container/
|
|
fi
|
|
|
|
port="${DIND_TEST_PORT:-32375}"
|
|
name="gitea-runner-dind-test-$$"
|
|
image="${DIND_TEST_IMAGE:-gitea-runner-${target}:dind-test}"
|
|
# The host daemon endpoint, captured before DOCKER_HOST is pointed at the fresh dind daemon.
|
|
host_docker="${DOCKER_HOST:-$(docker context inspect --format '{{.Endpoints.docker.Host}}')}"
|
|
test_dir=""
|
|
|
|
cleanup() {
|
|
docker -H "$host_docker" rm -fv "$name" >/dev/null 2>&1 || true
|
|
if [ -n "$test_dir" ]; then
|
|
rm -rf "$test_dir"
|
|
fi
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
if [ -z "${DIND_TEST_IMAGE:-}" ]; then
|
|
echo "==> Building ${target} image"
|
|
docker build --target "$target" -t "$image" .
|
|
fi
|
|
|
|
# Override the image entrypoint (s6) and run only dockerd, exposed over insecure TCP.
|
|
# We are testing the daemon the image ships, not the runner supervision tree.
|
|
#
|
|
# How the test process reaches the daemon depends on where it runs:
|
|
# - plain host: publish 2375 on loopback and connect to 127.0.0.1.
|
|
# - inside a container (CI), the daemon is a sibling container, so its published port is on
|
|
# the host, not our loopback; instead attach it to our own network and reach it by name.
|
|
self_container=""
|
|
if [ -f /.dockerenv ]; then
|
|
self_container="$(cat /proc/sys/kernel/hostname 2>/dev/null || cat /etc/hostname)"
|
|
fi
|
|
self_network=""
|
|
if [ -n "$self_container" ]; then
|
|
self_network="$(docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{$k}}{{"\n"}}{{end}}' "$self_container" 2>/dev/null | head -1)"
|
|
fi
|
|
|
|
# The two cases differ only in how the daemon is exposed and addressed; everything else
|
|
# (privileged, name, TLS-off entrypoint, image, --host) is shared, so collect just the
|
|
# differing run args and the resulting DOCKER_HOST here.
|
|
if [ -n "$self_network" ]; then
|
|
echo "==> Starting ${target} daemon on network ${self_network} (reached as ${name}:2375)"
|
|
run_args=(--network "$self_network")
|
|
daemon_host="tcp://${name}:2375"
|
|
else
|
|
echo "==> Starting ${target} daemon on tcp://127.0.0.1:${port}"
|
|
run_args=(-p "127.0.0.1:${port}:2375")
|
|
daemon_host="tcp://127.0.0.1:${port}"
|
|
fi
|
|
# Create the dind container on the host daemon first, then repoint DOCKER_HOST at it: exporting
|
|
# DOCKER_HOST before `docker run` would make this `docker run` target the not-yet-existent dind.
|
|
docker -H "$host_docker" run -d --privileged --name "$name" "${run_args[@]}" \
|
|
-e DOCKER_TLS_CERTDIR= \
|
|
--entrypoint dockerd-entrypoint.sh \
|
|
"$image" --host=tcp://0.0.0.0:2375 >/dev/null
|
|
export DOCKER_HOST="$daemon_host"
|
|
|
|
echo "==> Waiting for daemon"
|
|
for _ in $(seq 1 60); do
|
|
docker version --format 'server docker {{.Server.Version}}' 2>/dev/null && break
|
|
sleep 1
|
|
done
|
|
if ! docker version --format 'server docker {{.Server.Version}}'; then
|
|
docker -H "$host_docker" logs "$name" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Seed the fresh daemon with images the host already has (the CI job pulls them in the
|
|
# preceding `make test`), so the daemon-facing tests run without registry access.
|
|
echo "==> Seeding daemon with cached host images"
|
|
preload="${DIND_TEST_PRELOAD:-alpine:latest}"
|
|
job_image="${ACT_TEST_IMAGE:-node:24-bookworm-slim}"
|
|
if [ "$default_tests" = true ]; then
|
|
preload="$preload $job_image"
|
|
fi
|
|
for img in $preload; do
|
|
if docker -H "$host_docker" image inspect "$img" >/dev/null 2>&1; then
|
|
docker -H "$host_docker" save "$img" | docker load >/dev/null 2>&1 && echo " loaded $img" || true
|
|
fi
|
|
done
|
|
|
|
echo "==> Running tests against dind daemon"
|
|
go test "$@"
|
|
|
|
if [ "$default_tests" = true ]; then
|
|
if ! docker image inspect "$job_image" >/dev/null 2>&1; then
|
|
echo "mounted Docker test requires ${job_image}, pull it on the host before running this harness" >&2
|
|
exit 1
|
|
fi
|
|
test_dir="$(mktemp -d)"
|
|
echo "==> Building mounted Docker test for the dind container"
|
|
CGO_ENABLED=0 GOOS=linux GOARCH="$(docker -H "$host_docker" image inspect "$image" --format '{{.Architecture}}')" \
|
|
go test -c -o "$test_dir/runner.test" ./act/runner/
|
|
docker -H "$host_docker" exec "$name" mkdir -p /tmp/gitea-runner-proxy-test/testdata
|
|
tar -C "$test_dir" -cf - runner.test -C "$PWD/act/runner" testdata/docker-proxy | \
|
|
docker -H "$host_docker" exec -i "$name" tar -x -C /tmp/gitea-runner-proxy-test
|
|
socket="unix:///var/run/docker.sock"
|
|
users=(0)
|
|
if [ "$target" = dind-rootless ]; then
|
|
socket="unix:///run/user/1000/docker.sock"
|
|
users=(1000 0)
|
|
fi
|
|
for user in "${users[@]}"; do
|
|
proxy_mode="proxy"
|
|
if [ "$user" != 0 ]; then
|
|
proxy_mode="direct"
|
|
fi
|
|
echo "==> Running mounted Docker job inside ${target} as UID ${user}, expecting ${proxy_mode} access"
|
|
docker -H "$host_docker" exec --user "$user" -w /tmp/gitea-runner-proxy-test \
|
|
-e DOCKER_HOST="$socket" -e ACT_TEST_DOCKER_PROXY="$proxy_mode" -e ACT_TEST_IMAGE="$job_image" \
|
|
"$name" ./runner.test -test.v -test.run '^TestDockerProxyMountedJob$' -test.timeout 3m
|
|
done
|
|
fi
|