Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/nix/script-tests/agent-backend-env.sh
atlas 75e9e205e3 fetch the agent's backend env file from the store, seeded from the existing file
the agent's own root unit reads swarm/agents/<agent>/backend-env into /run and
creates it from backendEnvironmentFile with -cas=0 when the store holds nothing.
the shared agent policy gains create (not update) on that one leaf. the harness
and the subagent daemon load the old file first and the fetched one after it.
2026-10-28 01:24:29 +01:00

306 lines
8.5 KiB
Shell

# Cases for ./agent-backend-env.nix. Each case gets a fresh runtime directory,
# credentials directory, operator's file and store, runs the unit's script under
# the unit's `UMask=0077` with a clean environment, and asserts on the exit
# code, the output, the files left behind and the `bao` calls made.
set -uo pipefail
failures=0
count=0
fail() {
echo "FAILED: $case: $*" >&2
failures=$((failures + 1))
}
leaf=secret/swarm/agents/a1/backend-env
login_call="login -method=cert -token-only"
get_call="kv get -field=env $leaf"
# setup <description>
setup() {
case=$1
dir=$(mktemp -d)
rt=$dir/rt
creds=$dir/creds
log=$dir/bao.log
kv=$dir/kv
earlier=$dir/earlier.env
mkdir -m 0700 "$rt"
mkdir "$creds" "$kv"
printf cert >"$creds/hive-agent-bao-cert"
printf key >"$creds/hive-agent-bao-key"
: >"$log"
if ! sed -e "s|/run/hive-agent-backend-env/|$rt/|g" -e "s|$EARLIER_FILE|$earlier|g" \
"$UNIT_SCRIPT" >"$dir/script"; then
echo "cannot read the rendered script" >&2
exit 1
fi
chmod +x "$dir/script"
if grep -q '/run/' "$dir/script"; then fail "the script still names /run after the rewrite"; fi
if ! grep -qF "$earlier" "$dir/script"; then fail "the rewrite did not reach the operator's file"; fi
login_rc=0
deny_read=
deny_write=
race=
stored=$kv/$(printf '%s' "$leaf" | tr / _)
}
go() {
count=$((count + 1))
(
umask 0077
cd "$dir" || exit 99
exec env -i \
PATH="$FAKE_BAO_BIN:$UNIT_PATH" \
BAO_ADDR="$UNIT_BAO_ADDR" \
BAO_CLIENT_CERT="$creds/hive-agent-bao-cert" \
BAO_CLIENT_KEY="$creds/hive-agent-bao-key" \
CREDENTIALS_DIRECTORY="$creds" \
FAKE_BAO_LOG="$log" \
FAKE_BAO_KV="$kv" \
FAKE_BAO_LOGIN_RC="$login_rc" \
FAKE_BAO_DENY_READ="$deny_read" \
FAKE_BAO_DENY_WRITE="$deny_write" \
FAKE_BAO_RACE="$race" \
"$dir/script"
) >"$dir/out" 2>"$dir/err"
rc=$?
}
expect_rc() {
if [ "$rc" != "$1" ]; then fail "exit $rc, expected $1; stderr: $(<"$dir/err")"; fi
}
expect_err() {
if ! grep -qF -- "$1" "$dir/err"; then fail "stderr lacks '$1'; stderr: $(<"$dir/err")"; fi
}
expect_out() {
if ! grep -qF -- "$1" "$dir/out"; then fail "stdout lacks '$1'; stdout: $(<"$dir/out")"; fi
}
# expect_calls <line>... — the exact `bao` argument lines, in order.
expect_calls() {
local want
want=$(printf '%s\n' "$@")
if [ "$(<"$log")" != "${want%$'\n'}" ]; then fail "bao calls were '$(<"$log")', expected '$*'"; fi
}
expect_no_calls() {
if [ -s "$log" ]; then fail "bao was called: $(<"$log")"; fi
}
# expect_same <path> <expected text> — byte for byte, trailing newlines included.
expect_same() {
if [ ! -e "$1" ]; then
fail "$1 missing"
return
fi
if ! printf '%s' "$2" | cmp -s - "$1"; then fail "$1 holds '$(<"$1")', expected '$2'"; fi
}
expect_no_file() {
if [ -e "$1" ]; then fail "$1 left behind"; fi
}
# The operator's file, kept to compare after the run.
put_earlier() {
printf '%s' "$1" >"$earlier"
chmod 0600 "$earlier"
earlier_inode=$(stat -c %i "$earlier")
}
expect_earlier_untouched() {
expect_same "$earlier" "$1"
if [ "$(stat -c %i "$earlier")" != "$earlier_inode" ]; then fail "the operator's file was replaced"; fi
}
# A leaf holding <text> before the run.
put_leaf() {
printf '%s' "$1" >"$stored"
}
expect_leaf() {
expect_same "$stored" "$1"
}
file_text=$'ANTHROPIC_API_KEY=k-file\nANTHROPIC_BASE_URL=http://x\n'
setup "no store identity delivered"
: >"$creds/hive-agent-bao-cert"
put_earlier "$file_text"
go
expect_rc 0
expect_err "has no store identity"
expect_no_calls
expect_no_file "$rt/env"
setup "a store holding nothing is seeded from the file, bytes unchanged, and the store's value is used"
put_earlier "$file_text"
go
expect_rc 0
expect_calls "$login_call" "$get_call" "kv put -cas=0 $leaf env=@$earlier" "$get_call"
expect_leaf "$file_text"
expect_same "$rt/env" "$file_text"
expect_earlier_untouched "$file_text"
if [ "$(stat -c %a "$rt/env")" != 600 ]; then fail "env is mode $(stat -c %a "$rt/env"), expected 600"; fi
expect_no_file "$rt/env.new"
expect_no_file "$rt/bao.err"
setup "a file with no final newline is seeded exactly"
put_earlier 'ANTHROPIC_API_KEY=k-file'
go
expect_rc 0
expect_leaf 'ANTHROPIC_API_KEY=k-file'
expect_same "$rt/env" 'ANTHROPIC_API_KEY=k-file'
expect_earlier_untouched 'ANTHROPIC_API_KEY=k-file'
setup "a value in the store wins over the file and is not written back to it"
put_earlier "$file_text"
put_leaf $'ANTHROPIC_API_KEY=k-bao\n'
go
expect_rc 0
expect_calls "$login_call" "$get_call"
expect_leaf $'ANTHROPIC_API_KEY=k-bao\n'
expect_same "$rt/env" $'ANTHROPIC_API_KEY=k-bao\n'
expect_earlier_untouched "$file_text"
setup "an unchanged store value leaves the file in place"
put_leaf "$file_text"
printf '%s' "$file_text" >"$rt/env"
chmod 0600 "$rt/env"
before=$(stat -c %i "$rt/env")
go
expect_rc 0
expect_out "is unchanged"
expect_same "$rt/env" "$file_text"
if [ "$(stat -c %i "$rt/env")" != "$before" ]; then fail "the unchanged env was replaced"; fi
setup "a changed store value replaces the file in place"
put_leaf $'ANTHROPIC_API_KEY=k-new\n'
printf old >"$rt/env"
chmod 0600 "$rt/env"
go
expect_rc 0
expect_out "fetched this agent's backend environment"
expect_same "$rt/env" $'ANTHROPIC_API_KEY=k-new\n'
expect_no_file "$rt/env.new"
setup "no file and nothing in the store writes nothing"
go
expect_rc 0
expect_out "no file to create it from"
expect_calls "$login_call" "$get_call"
expect_no_file "$rt/env"
expect_no_file "$stored"
setup "an empty file is never stored"
put_earlier ""
go
expect_rc 0
expect_calls "$login_call" "$get_call"
expect_no_file "$rt/env"
expect_no_file "$stored"
setup "nothing in the store leaves an env fetched earlier alone"
printf kept >"$rt/env"
chmod 0600 "$rt/env"
go
expect_rc 0
expect_same "$rt/env" kept
setup "a store that refuses the login with a file in place changes nothing and fails the unit"
put_earlier "$file_text"
login_rc=2
go
expect_rc 1
expect_err "did not accept this agent's certificate login"
expect_err "no such host"
expect_calls "$login_call"
expect_no_file "$rt/env"
expect_no_file "$stored"
expect_earlier_untouched "$file_text"
setup "a lost race reads back the winner's value and keeps the file"
put_earlier "$file_text"
race=$'ANTHROPIC_API_KEY=k-winner\n'
go
expect_rc 0
expect_calls "$login_call" "$get_call" "kv put -cas=0 $leaf env=@$earlier" "$get_call"
expect_err "could not create $leaf"
expect_leaf $'ANTHROPIC_API_KEY=k-winner\n'
expect_same "$rt/env" $'ANTHROPIC_API_KEY=k-winner\n'
expect_earlier_untouched "$file_text"
setup "a policy that refuses the create leaves the file to carry the agent"
put_earlier "$file_text"
deny_write=1
go
expect_rc 0
expect_calls "$login_call" "$get_call" "kv put -cas=0 $leaf env=@$earlier" "$get_call"
expect_err "permission denied"
expect_err "holds no usable env"
expect_no_file "$rt/env"
expect_no_file "$stored"
expect_earlier_untouched "$file_text"
setup "a leaf that exists but holds no env is not overwritten"
put_earlier "$file_text"
put_leaf ""
go
expect_rc 0
expect_calls "$login_call" "$get_call"
expect_err "holds no usable env"
expect_leaf ""
expect_no_file "$rt/env"
setup "a leaf without the env field is not overwritten"
put_earlier "$file_text"
put_leaf "x"
printf other >"$stored.field"
go
expect_rc 0
expect_calls "$login_call" "$get_call"
expect_leaf "x"
expect_no_file "$rt/env"
setup "a refused read fails the unit and writes nothing"
put_earlier "$file_text"
deny_read=1
go
expect_rc 1
expect_err "could not read $leaf"
expect_err "permission denied"
expect_calls "$login_call" "$get_call"
expect_no_file "$rt/env"
expect_earlier_untouched "$file_text"
# `UMask=0077` makes every file the script creates 0600; a stale leftover of a
# stopped run must not block the next.
setup "leftovers of a stopped run do not block the fetch"
put_leaf $'ANTHROPIC_API_KEY=k-bao\n'
printf stale >"$rt/env.new"
printf stale >"$rt/bao.err"
go
expect_rc 0
expect_same "$rt/env" $'ANTHROPIC_API_KEY=k-bao\n'
expect_no_file "$rt/env.new"
expect_no_file "$rt/bao.err"
# The control: the stub's create-only answer is what the lost-race and
# refused-create cases lean on, so show it refuses a second create.
setup "the stub refuses a create over an existing leaf"
put_leaf kept
printf x >"$dir/x"
(
PATH="$FAKE_BAO_BIN:$PATH" BAO_TOKEN=s.fake FAKE_BAO_LOG=$log FAKE_BAO_KV=$kv \
bao kv put -cas=0 "$leaf" env=@"$dir/x"
) >/dev/null 2>"$dir/err"
if [ $? -eq 0 ]; then fail "the stub took a second create"; fi
expect_leaf kept
if [ "$failures" -gt 0 ]; then
echo "script-test-agent-backend-env: $failures failed assertions over $count runs" >&2
exit 1
fi
echo "script-test-agent-backend-env: $count runs, all assertions hold"