Diagnostic only, never to be merged. Since #232 no workflow calls the Actions cache, so nothing has shown whether a job container can reach the cache server today. This job prints the cache address the runner advertises (path redacted, since job logs are public), probes it over TCP next to other ports and host addresses, and round-trips a tiny payload through actions/cache. It is green only if the payload comes back. Refs #234 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
144 lines
5.8 KiB
YAML
144 lines
5.8 KiB
YAML
name: Diag — cache server reachability (#234)
|
|
|
|
# THROWAWAY DIAGNOSTIC — never merge. Lives on its own branch for #234.
|
|
#
|
|
# Since #232 no workflow calls the Actions cache: in July both the restore
|
|
# (getCacheEntry) and the save (reserveCache) ended in "Request timeout". The
|
|
# suspected cause is that the job container cannot reach the cache address the
|
|
# runner hands it. This job checks that from inside a job container, without
|
|
# host access:
|
|
# 1. which cache address the runner advertises (path redacted: it may carry a
|
|
# per-run token, and this repository's job logs are public);
|
|
# 2. raw TCP reachability of that address, next to other ports and other host
|
|
# addresses, to tell "dropped" from "refused" from "open";
|
|
# 3. an end-to-end round trip through actions/cache with a tiny payload.
|
|
# The job is green only if the payload comes back from the cache server.
|
|
#
|
|
# Steps that use bash features set `shell: bash`: inside a container the runner
|
|
# defaults to `sh`, and a job-level `defaults` block is refused by the runner.
|
|
|
|
on:
|
|
pull_request:
|
|
paths:
|
|
- '.forgejo/workflows/diag-cache-probe.yml'
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
probe:
|
|
runs-on: ubuntu
|
|
container: ubuntu:22.04
|
|
timeout-minutes: 20
|
|
steps:
|
|
- name: Cache address and TCP reachability
|
|
shell: bash
|
|
run: |
|
|
echo "Runner-provided variables (names only):"
|
|
compgen -e | grep -E 'ACTIONS|RUNNER|FORGEJO|GITEA' | sort | sed 's/^/ /'
|
|
compgen -e | grep -E '_RUNNER_VERSION$' | while read -r v; do echo " $v=${!v}"; done
|
|
|
|
url="${ACTIONS_CACHE_URL:-}"
|
|
if [ -z "$url" ]; then
|
|
echo "ACTIONS_CACHE_URL is empty: the runner gives this job no cache server."
|
|
exit 0
|
|
fi
|
|
base=$(printf '%s' "$url" | sed -E 's#^(https?://[^/]+).*#\1#')
|
|
scheme=${base%%://*}
|
|
hostport=${base#*://}
|
|
case "$hostport" in
|
|
\[*\]:*) host=${hostport%]:*}; host=${host#[}; port=${hostport##*]:} ;;
|
|
*:*) host=${hostport%:*}; port=${hostport##*:} ;;
|
|
*) host=$hostport; port="" ;;
|
|
esac
|
|
if [ -z "$port" ]; then
|
|
if [ "$scheme" = https ]; then port=443; else port=80; fi
|
|
fi
|
|
shown=$host
|
|
case "$host" in *:*) shown="[$host]" ;; esac
|
|
echo
|
|
echo "Cache address advertised by the runner: $scheme://$shown:$port/<path redacted>"
|
|
forge=$(getent ahostsv4 git.lacompagniemaximus.com | awk '{ print $1; exit }')
|
|
echo "git.lacompagniemaximus.com resolves to (IPv4): ${forge:-unresolved}"
|
|
echo "Container addresses: $(hostname -I 2>/dev/null || echo unknown)"
|
|
gw_hex=$(awk '$2 == "00000000" { print $3; exit }' /proc/net/route)
|
|
gw=""
|
|
if [ -n "$gw_hex" ]; then
|
|
gw=$(printf '%d.%d.%d.%d' "0x${gw_hex:6:2}" "0x${gw_hex:4:2}" "0x${gw_hex:2:2}" "0x${gw_hex:0:2}")
|
|
fi
|
|
echo "Default gateway (the host, seen from this job network): ${gw:-unknown}"
|
|
|
|
probe() {
|
|
local label=$1 h=$2 p=$3 start ms rc err verdict
|
|
start=$(date +%s%N)
|
|
err=$(timeout 10 bash -c "exec 3<>/dev/tcp/$h/$p" 2>&1)
|
|
rc=$?
|
|
ms=$(( ($(date +%s%N) - start) / 1000000 ))
|
|
case $rc in
|
|
0) verdict="OPEN" ;;
|
|
124) verdict="TIMEOUT" ;;
|
|
*) case "$err" in
|
|
*refused*) verdict="REFUSED" ;;
|
|
*) verdict="ERROR (${err##*: })" ;;
|
|
esac ;;
|
|
esac
|
|
printf ' %-36s %-26s %-8s %6d ms\n' "$label" "$h:$p" "$verdict" "$ms"
|
|
}
|
|
|
|
echo
|
|
echo "TCP probes, 10 s each. OPEN = handshake done; REFUSED = the host answered"
|
|
echo "but nothing listens there; TIMEOUT = no answer (packets dropped, or the"
|
|
echo "address is unreachable from this network)."
|
|
probe "advertised cache address" "$host" "$port"
|
|
probe "same host, port 22 (sshd)" "$host" 22
|
|
probe "same host, port 443 (Traefik)" "$host" 443
|
|
if [ -n "$gw" ]; then
|
|
probe "job network gateway, cache port" "$gw" "$port"
|
|
probe "job network gateway, port 22" "$gw" 22
|
|
fi
|
|
probe "docker0 172.17.0.1, cache port" 172.17.0.1 "$port"
|
|
|
|
- name: Install Node.js 20
|
|
run: |
|
|
apt-get update
|
|
apt-get install -y --no-install-recommends curl ca-certificates
|
|
# actions/cache is a JavaScript action: it needs `node` in the container PATH.
|
|
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
|
|
apt-get install -y nodejs
|
|
node --version
|
|
|
|
- name: Write a tiny payload
|
|
id: payload
|
|
shell: bash
|
|
run: |
|
|
key="diag-234-$(date -u +%Y%m%dT%H%M%SZ)-$RANDOM"
|
|
mkdir -p probe-payload
|
|
printf '%s' "$key" > probe-payload/stamp
|
|
echo "key=$key" >> "$GITHUB_OUTPUT"
|
|
echo "payload key: $key"
|
|
|
|
- name: Save it (actions/cache/save)
|
|
uses: https://github.com/actions/cache/save@v4
|
|
with:
|
|
path: probe-payload
|
|
key: ${{ steps.payload.outputs.key }}
|
|
|
|
- name: Delete the local copy
|
|
run: rm -rf probe-payload
|
|
|
|
- name: Restore it (actions/cache/restore, fails on a miss)
|
|
uses: https://github.com/actions/cache/restore@v4
|
|
with:
|
|
path: probe-payload
|
|
key: ${{ steps.payload.outputs.key }}
|
|
fail-on-cache-miss: true
|
|
|
|
- name: Check the payload made the round trip
|
|
shell: bash
|
|
run: |
|
|
if [ "$(cat probe-payload/stamp 2>/dev/null)" = "${{ steps.payload.outputs.key }}" ]; then
|
|
echo "ROUND TRIP OK: the payload came back from the cache server."
|
|
else
|
|
echo "ROUND TRIP FAILED: the payload is missing or different."
|
|
exit 1
|
|
fi
|