Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/nix/script-tests/agent-hive-facts.sh
atlas f3c0a87c0f hand agents the queue address at container start
HIVE_AGENT_NATS_URL now reaches each container in its hive facts, from
the host's deploy.hive-controller.queue.agentNatsUrl, instead of being
rendered into the meta flake. The agent option
services.hyperhive.agent.queue.natsUrl is removed; the harness unit
imports the queue credential whenever the token endpoint is set.

On deploy every agent rebuilds once (the rendered flake loses the
address), and the harness reads the address from the facts file
hive-priv writes before each start.

Refs #4857
2026-10-24 21:13:24 +02:00

134 lines
6.1 KiB
Shell

# Cases for ./agent-hive-facts.nix. Each case writes a facts file (or none),
# runs the generator with a clean environment, and compares its whole output;
# then sources the login-shell snippet under each POSIX shell and compares what
# a child process sees.
set -uo pipefail
failures=0
count=0
fail() {
echo "FAILED: $case: $*" >&2
failures=$((failures + 1))
}
dir=$(mktemp -d)
facts=$dir/facts
sed "s|/etc/hyperhive-hive-facts|$facts|g" "$GENERATOR" >"$dir/generator"
chmod +x "$dir/generator"
sed "s|$GENERATOR|$dir/generator|g" "$PROFILE" >"$dir/profile"
envbin=$(command -v env)
case="setup"
if grep -q '/etc/hyperhive-hive-facts' "$dir/generator"; then fail "the generator still names /etc after the rewrite"; fi
if ! grep -qF "$facts" "$dir/generator"; then fail "the rewrite did not reach the generator's read"; fi
if ! grep -qF "$dir/generator" "$dir/profile"; then fail "the snippet does not call the rewritten generator"; fi
if ! printf '%s\n' "$EXTRA_INIT" | grep -qxF '. /etc/hyperhive/hive-facts.sh'; then
fail "/etc/set-environment does not source the snippet"
fi
# check <description> <expected generator output>
check() {
case=$1
count=$((count + 1))
local want=$2 got
got=$(env -i "$dir/generator")
if [ "$got" != "$want" ]; then
fail "generator printed:"$'\n'"$got"$'\n'"wanted:"$'\n'"$want"
fi
local sh
for sh in $SHELLS; do
# Only what the snippet exported reaches the child's environment. No PATH:
# the snippet must not need one.
got=$(env -i "$sh" -c ". '$dir/profile'; exec '$envbin'" | grep -E '^(HYPERHIVE_|HIVE_DASHBOARD_PORT=|HIVE_AGENT_NATS_URL=|CLAUDE_|LD_PRELOAD=|__hyperhive)' | sort)
if [ "$got" != "$(printf '%s\n' "$want" | sort)" ]; then
fail "$sh: a child of the sourcing shell saw:"$'\n'"$got"
fi
done
}
printf 'HYPERHIVE_HIVE_NAME=pr1ma\nHYPERHIVE_HIVE_DOMAIN=pr1ma.example.test\n' >"$facts"
check "both facts" "HYPERHIVE_HIVE_NAME=pr1ma
HYPERHIVE_HIVE_DOMAIN=pr1ma.example.test
CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX=pr1ma-a1"
printf 'LD_PRELOAD=/x\nHYPERHIVE_HIVE_NAME=pr1ma\nPATH=/evil\n' >"$facts"
check "a planted key is dropped" "HYPERHIVE_HIVE_NAME=pr1ma
CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX=pr1ma-a1"
printf 'HYPERHIVE_HIVE_DOMAIN=pr1ma.example.test\nHYPERHIVE_HIVE_NAME=pr1ma' >"$facts"
check "a last line without a newline is kept" "HYPERHIVE_HIVE_DOMAIN=pr1ma.example.test
HYPERHIVE_HIVE_NAME=pr1ma
CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX=pr1ma-a1"
printf 'HIVE_DASHBOARD_PORT=7123\nHYPERHIVE_CLAUDE_CODE_PATH=/nix/store/0abc-claude-code\nHYPERHIVE_OTEL_EXTRA_RESOURCE_ATTRIBUTES=team=a,env=prod\nHIVE_AGENT_NATS_URL=tls://nats.example.test:4222\n' >"$facts"
check "the per-host values, a value holding = kept whole" "HIVE_DASHBOARD_PORT=7123
HYPERHIVE_CLAUDE_CODE_PATH=/nix/store/0abc-claude-code
HYPERHIVE_OTEL_EXTRA_RESOURCE_ATTRIBUTES=team=a,env=prod
HIVE_AGENT_NATS_URL=tls://nats.example.test:4222
CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX=unknown-a1"
: >"$facts"
check "an empty file (no facts)" "CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX=unknown-a1"
rm -f "$facts"
check "no file" "CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX=unknown-a1"
# OTEL_RESOURCE_ATTRIBUTES, for units (the generator, run with what the
# hive-facts generator printed) and for login shells (the extraInit line, after
# the snippet). The base labels come from the build; the hive's are appended.
base="service.name=hyperhive-agent,agent=a1,swarm=unknown"
otel_check() {
case=$1
count=$((count + 1))
local want="OTEL_RESOURCE_ATTRIBUTES=$2" got sh passed
mapfile -t passed < <(env -i "$dir/generator")
got=$(env -i "${passed[@]}" "$OTEL_GENERATOR")
if [ "$got" != "$want" ]; then fail "generator printed: $got, wanted: $want"; fi
for sh in $SHELLS; do
got=$(env -i "$sh" -c ". '$dir/profile'; $otel_init; exec '$envbin'" | grep '^OTEL_RESOURCE_ATTRIBUTES=')
if [ "$got" != "$want" ]; then fail "$sh: a login shell saw: $got"; fi
done
}
case="otel setup"
otel_init=$(printf '%s\n' "$EXTRA_INIT" | grep -F "$OTEL_GENERATOR")
if [ -z "$otel_init" ]; then fail "/etc/set-environment does not run the OTEL resource generator"; fi
if [ "$(printf '%s\n' "$EXTRA_INIT" | grep -n . | grep -F "$OTEL_GENERATOR" | cut -d: -f1)" -lt \
"$(printf '%s\n' "$EXTRA_INIT" | grep -nxF '. /etc/hyperhive/hive-facts.sh' | cut -d: -f1)" ]; then
fail "/etc/set-environment runs the OTEL resource generator before the hive-facts snippet"
fi
printf 'HYPERHIVE_OTEL_EXTRA_RESOURCE_ATTRIBUTES=team=a,env=prod\n' >"$facts"
otel_check "the hive's labels are appended" "$base,team=a,env=prod"
rm -f "$facts"
otel_check "no hive labels: the base alone, no trailing comma" "$base"
# `claude`: the hive's pinned build when it is there, this container's own
# otherwise. Both builds are stand-ins that print which one ran.
mkdir -p "$dir/own/bin" "$dir/pinned/bin"
printf '#!%s\necho own "$@"\n' "$BASH" >"$dir/own/bin/claude"
printf '#!%s\necho pinned "$@"\n' "$BASH" >"$dir/pinned/bin/claude"
chmod +x "$dir/own/bin/claude" "$dir/pinned/bin/claude"
printf '%s' "$CLAUDE_WRAPPER" | sed -E "s|/nix/store/[^ ]+/bin/claude|$dir/own/bin/claude|" >"$dir/claude"
case="claude setup"
if ! grep -qF "$dir/own/bin/claude" "$dir/claude"; then fail "the rewrite did not reach the fallback claude"; fi
# claude_check <description> <pin or ""> <which ran> <expected stderr>
claude_check() {
case=$1
count=$((count + 1))
local got said pin=()
if [ -n "$2" ]; then pin=("HYPERHIVE_CLAUDE_CODE_PATH=$2"); fi
# Under this shell: the shebang names the build's bash, not in this sandbox.
got=$(env -i "${pin[@]}" "$BASH" "$dir/claude" -p hi 2>"$dir/stderr")
said=$(cat "$dir/stderr")
if [ "$got" != "$3 -p hi" ]; then fail "ran: $got, wanted: $3 -p hi"; fi
if [ "$said" != "$4" ]; then fail "said on stderr: '$said', wanted: '$4'"; fi
}
claude_check "the pinned claude when the hive hands one over, silently" "$dir/pinned" pinned ""
claude_check "the container's own claude with no pin, silently" "" own ""
claude_check "the container's own claude when the pinned path is gone, saying so" "$dir/gone" own \
"claude: $dir/gone/bin/claude, the hive's pinned claude-code, is missing; running this container's own"
echo "$count cases, $failures failed"
[ "$failures" -eq 0 ]