Something went wrong. Try again.
Backend environment for match hosting for lance.blue
Something went wrong. Try again.
Shell
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148#!/usr/bin/env bash# 30-assets.sh fetches the per-match files. Two things here are easy to break and# invisible until a real launch:## - $MM_HOME/userdata is a symlink onto the tmpfs, and mkdir -p cannot create# through a dangling symlink. The image ships it dangling on purpose.# - a camo that fails to download must not end the match. It is cosmetic, and# `fetch` is fatal by design, so the camo path has to use try_fetch.# - the index is what the match host paints from, so a camo that failed its# digest must not be named in it. A file that is present but wrong is worse# than one that is absent: absent is default colours, wrong is whatever the# bytes turned out to be.set -uo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"TMP="$(mktemp -d)"trap 'rm -rf "$TMP"' EXIT
pass=0; fail=0check() { if "${@:2}"; then echo "ok $1"; pass=$((pass+1)); else echo "FAIL $1"; fail=$((fail+1)); fi; }
# Stage the image's layout: a MegaMek home whose userdata dangles onto the tmpfs.mkdir -p "$TMP/run" "$TMP/mm"ln -s "$TMP/run/userdata" "$TMP/mm/userdata"
mkdir -p "$TMP/mm/data/scenarios"printf 'MMSVersion=1\nFactions=A,B\n' > "$TMP/mm/data/scenarios/scenario.mms"
run_assets() { # <manifest-json> local run="$TMP/run" # A container starts with an empty tmpfs. Without this, files staged by an # earlier case in this file are still there and every count below is of the # whole session rather than of the run being checked. rm -rf "$run/camo" printf '%s' "$1" > "$run/manifest.json" ARENA_HOME="$ROOT" ARENA_RUN="$run" MM_HOME="$TMP/mm" \ bash "$ROOT/container/init/30-assets.sh" 2>&1}
MANIFEST="$(jq -nc \ '{version:1, matchId:"t", scenario:{name:"scenario.mms"}, players:[{slot:"A",control:"human",did:"did:plc:x"},{slot:"B",control:"bot"}]}')"
out="$(run_assets "$MANIFEST")"; status=$?check "runs against a dangling userdata symlink" test "$status" -eq 0[ "$status" -eq 0 ] || printf '%s\n' "$out" | sed 's/^/ /'
check "userdata target created through the symlink, on the tmpfs" \ test -d "$TMP/run/userdata"check "scenario fetched" test -f "$TMP/run/scenario/match.mms"check "scenario.env written" grep -q 'ARENA_SCENARIO=' "$TMP/run/scenario.env"# Which fight this is, for the result document: the staged path is match.mms# whichever form the manifest used, so the source and the library path are the# only things that say what was played.check "and it says the scenario came from the library" \ grep -qx 'ARENA_SCENARIO_SOURCE=library' "$TMP/run/scenario.env"check "and which one it picked" \ grep -qx 'ARENA_SCENARIO_LIBRARY=scenario.mms' "$TMP/run/scenario.env"
# A scenario nobody baked into the image: the manifest carries the file itself.# This is what a generated fight - a daily challenge - is launched from.INLINE="$(jq -nc \ '{version:1, matchId:"t", scenario:{content:"MMSVersion=1\nName=Carried\nFactions=A,B\n"}, players:[{slot:"A",control:"human",did:"did:plc:x"},{slot:"B",control:"bot"}]}')"run_assets "$INLINE" >/dev/null 2>&1check "a scenario carried by the manifest is staged" \ grep -qx 'Name=Carried' "$TMP/run/scenario/match.mms"check "and it is what the host is pointed at" \ grep -q "ARENA_SCENARIO=$TMP/run/scenario/match.mms" "$TMP/run/scenario.env"check "a carried scenario is named as carried" \ grep -qx 'ARENA_SCENARIO_SOURCE=manifest' "$TMP/run/scenario.env"# There is no library path for a scenario that was never in a library, and an# invented one would point at a file this image does not have.check "and names no library path" \ grep -qx "ARENA_SCENARIO_LIBRARY=''" "$TMP/run/scenario.env"
# Both, which is a manifest disagreeing with itself about which fight this is.# Picking one would hand somebody the wrong match.BOTH="$(jq -nc \ '{version:1, matchId:"t", scenario:{name:"scenario.mms", content:"MMSVersion=1\nFactions=A,B\n"}, players:[{slot:"A",control:"human",did:"did:plc:x"},{slot:"B",control:"bot"}]}')"out="$(run_assets "$BOTH" 2>&1)" && status=0 || status=$?check "a manifest with both a name and content is refused" test "$status" -ne 0check "and it says which two it was given" \ grep -q 'exactly one' <<< "$out"check "a manifest with no camo leaves an empty index" \ test ! -s "$TMP/run/camo/index.tsv"
# Cosmetic, so it must not be fatal - fetch() exits the script, try_fetch does not.CAMO="$(jq -nc \ '{version:1, matchId:"t", scenario:{name:"scenario.mms"}, players:[{slot:"A",control:"human",did:"did:plc:x", camo:{url:"file:///nonexistent/nope.png", sha256:"dead"}}, {slot:"B",control:"bot"}]}')"run_assets "$CAMO" >/dev/null 2>&1check "an unfetchable camo does not end the match" test $? -eq 0check "an unfetchable camo is not named in the index" \ test ! -s "$TMP/run/camo/index.tsv"
# A camo that arrives whole: named in the index, under the slot the scenario# spells, pointing at the file that was actually written.printf 'not really a png' > "$TMP/real.png"REAL_SHA="$(sha256sum "$TMP/real.png" | cut -d' ' -f1)"GOOD="$(jq -nc --arg url "file://$TMP/real.png" --arg sha "$REAL_SHA" \ '{version:1, matchId:"t", scenario:{name:"scenario.mms"}, players:[{slot:"A",control:"human",did:"did:plc:x", camo:{url:$url, sha256:$sha}}, {slot:"B",control:"bot"}]}')"run_assets "$GOOD" >/dev/null 2>&1check "a good camo is indexed under its slot" \ grep -qx "$(printf 'A\t1-A.png')" "$TMP/run/camo/index.tsv"check "the indexed file is the one on disk" test -f "$TMP/run/camo/1-A.png"check "the bot slot is not in the index" \ test "$(wc -l < "$TMP/run/camo/index.tsv")" -eq 1
# Two slots that sanitise to the same filename. Before the counter, the second# download landed on the first one's file - so one player's camo was painted# onto the other player's units, which is the one thing this path must not do.COLLIDE="$(jq -nc --arg url "file://$TMP/real.png" --arg sha "$REAL_SHA" \ '{version:1, matchId:"t", scenario:{name:"scenario.mms"}, players:[{slot:"Team A",control:"human",did:"did:plc:x", camo:{url:$url, sha256:$sha}}, {slot:"Team_A",control:"human",did:"did:plc:y", camo:{url:$url, sha256:$sha}}]}')"run_assets "$COLLIDE" >/dev/null 2>&1check "two slots that sanitise alike get two files" \ test "$(ls "$TMP"/run/camo/*.png | wc -l)" -eq 2check "and two index lines, one per slot" \ test "$(wc -l < "$TMP/run/camo/index.tsv")" -eq 2check "each index line names a file that exists" \ bash -c 'cd "$1/run/camo" && while IFS=$'"'"'\t'"'"' read -r _ f; do [ -f "$f" ] || exit 1; done < index.tsv' _ "$TMP"
# The same bytes under a digest that does not match them.BAD="$(jq -nc --arg url "file://$TMP/real.png" \ '{version:1, matchId:"t", scenario:{name:"scenario.mms"}, players:[{slot:"A",control:"human",did:"did:plc:x", camo:{url:$url, sha256:"00"}}, {slot:"B",control:"bot"}]}')"run_assets "$BAD" >/dev/null 2>&1check "a camo that fails its digest is not indexed" \ test ! -s "$TMP/run/camo/index.tsv"
echoecho "$pass passed, $fail failed"[ "$fail" -eq 0 ]