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
134 lines
6.1 KiB
Shell
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 ]
|