Files
DDI/test/e2e/run.sh
T
beug.k@beugsiud.deandClaude Opus 5.5 e7a0fbb123 test(e2e): pending changes, manual mode with push, diff, rollback, events
New steps before the revoke step: /config/pending reports nothing after
revision 2 and the new address afterwards; the DHCP agent switched to
manual mode stays on revision 2 while the DNS agent reaches 3, and an
explicit push of revision 3 brings it there with a succeeded push
deployment; the diff of revision 3 contains the new host; rolling back
to revision 1 creates revision 4 with SOA serial 4 and without the later
names; /events announces a revision created while listening.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:11:27 +02:00

176 lines
5.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# SPDX-License-Identifier: Apache-2.0
#
# End-to-end test of the M3 path: API change -> revision -> agent fetches,
# verifies and applies -> BIND/Kea serve it; and of M4: pending changes,
# manual mode with explicit push, diff, rollback and live events. Needs
# Docker with compose and the binaries from `make build` (BIN_DIR, default
# bin/).
#
# Usage: test/e2e/run.sh (make test-e2e)
# Environment: BIN_DIR, KEEP=1 keeps the containers after the run.
set -euo pipefail
here="$(cd "$(dirname "$0")" && pwd)"
root="$(cd "$here/../.." && pwd)"
bin_dir="${BIN_DIR:-$root/bin}"
case "$bin_dir" in /*) ;; *) bin_dir="$root/$bin_dir" ;; esac
dc() { docker compose -f "$here/compose.yaml" "$@"; }
api() { dc exec -T server /e2e/api.sh "$@"; }
step() { printf '\n=== %s\n' "$*"; }
fail() {
echo "$*" >&2
exit 1
}
cleanup() {
local rc=$?
if [[ $rc -ne 0 ]]; then
echo "FAILED (exit $rc); logs follow" >&2
dc logs --no-color --tail 300 >&2 || true
for svc in bind kea; do
echo "--- agent log ($svc)" >&2
dc exec -T "$svc" sh -c 'cat /tmp/agent.log 2>/dev/null' >&2 || true
done
fi
if [[ "${KEEP:-}" != 1 ]]; then
dc down -v --remove-orphans >/dev/null 2>&1 || true
fi
rm -rf "${here:?}/bin"
}
trap cleanup EXIT
step "Build images"
mkdir -p "$here/bin"
for b in ddi-server ddi-agent-dns ddi-agent-dhcp; do
cp "$bin_dir/$b" "$here/bin/$b"
done
for _ in 1 2 3; do dc build --pull && break; sleep 10; done
dc up -d --wait
step "Set up data through the API"
password="$(dc exec -T server ddi-server bootstrap-admin | sed -n 's/^password: //p')"
[[ -n "$password" ]] || { echo "bootstrap-admin printed no password" >&2; exit 1; }
api setup "$password"
step "Enroll the agents"
dns_cmd="$(api token dns)"
dhcp_cmd="$(api token dhcp)"
[[ "$dns_cmd" == *'--ca-hash sha256:'* ]] || { echo "unexpected command: $dns_cmd" >&2; exit 1; }
# The commands come from our own API and are run as argument lists.
read -r -a dns_args <<<"$dns_cmd"
read -r -a dhcp_args <<<"$dhcp_cmd"
dc exec -T bind "${dns_args[@]}"
dc exec -T kea "${dhcp_args[@]}"
dc exec -T -d bind sh -c 'exec ddi-agent-dns run >/tmp/agent.log 2>&1'
dc exec -T -d kea sh -c 'exec ddi-agent-dhcp run >/tmp/agent.log 2>&1'
step "Revision 1 reaches both agents"
rev="$(api revision "initial data")"
[[ "$rev" == 1 ]] || { echo "first revision is $rev" >&2; exit 1; }
api wait-applied 1 2
dig_short() { dc exec -T bind dig @127.0.0.1 "$@" +short +time=2 +tries=3; }
expect() {
local want="$1"
shift
local got
got="$("$@")"
if [[ "$got" != "$want" ]]; then
echo "expected '$want', got '$got' from: $*" >&2
exit 1
fi
echo "ok $* -> $got"
}
soa_serial() {
local soa
soa="$(dig_short e2e.test SOA)"
awk '{print $3}' <<<"$soa"
}
expect "192.0.2.10" dig_short www.e2e.test A
expect "www.e2e.test." dig_short -x 192.0.2.10
expect "1" soa_serial
dc exec -T kea grep -q '"hw-address": "00:11:22:33:44:55"' /etc/kea/kea-dhcp4.conf
echo "ok Kea configuration contains the reservation"
step "Changing an address deploys revision 2"
api address 192.0.2.20 mail
rev="$(api revision "mail server")"
[[ "$rev" == 2 ]] || { echo "second revision is $rev" >&2; exit 1; }
api wait-applied 2 2
expect "192.0.2.20" dig_short mail.e2e.test A
expect "mail.e2e.test." dig_short -x 192.0.2.20
step "Unchanged data creates no revision"
rev="$(api revision "nothing changed")"
[[ "$rev" == 2 ]] || { echo "unchanged data produced revision $rev" >&2; exit 1; }
step "Deployments were reported"
api deployments | grep -c '"outcome": "succeeded"' | {
read -r n
[[ "$n" -ge 4 ]] || { echo "only $n successful deployments" >&2; exit 1; }
echo "ok $n successful deployments"
}
step "Pending changes are reported (M4)"
pending="$(api pending)"
read -r changed changes files <<<"$pending"
[[ "$changed" == false ]] || fail "pending changes right after revision 2: changed=$changed changes=$changes files=$files"
echo "ok nothing pending after revision 2"
api address 192.0.2.30 ftp
pending="$(api pending)"
read -r changed changes files <<<"$pending"
[[ "$changed" == true && "$changes" -ge 1 && "$files" -ge 1 ]] ||
fail "new address not pending: changed=$changed changes=$changes files=$files"
echo "ok $changes pending change(s) in $files file(s)"
step "Manual mode: the DHCP agent waits for an explicit push"
api mode dhcp manual
api wait-mode dhcp manual
rev="$(api revision "ftp server")"
[[ "$rev" == 3 ]] || fail "third revision is $rev"
api wait-agent dns 3
expect "192.0.2.30" dig_short ftp.e2e.test A
sleep 5
got="$(api agent-revision dhcp)"
[[ "$got" == 2 ]] || fail "DHCP agent in manual mode runs revision $got instead of 2"
echo "ok DHCP agent stays on revision 2"
step "Pushing revision 3 to the DHCP agent"
dep="$(api deploy 3 dhcp)"
[[ "$dep" =~ ^[0-9]+$ ]] || fail "unexpected deployment ID '$dep'"
api wait-agent dhcp 3
api wait-deployment "$dep" succeeded
echo "ok push deployment $dep succeeded"
step "Diff of revision 3 shows the new host"
n="$(api diff-added 3 ftp)"
[[ "$n" -ge 1 ]] || fail "diff of revision 3 has no added line with ftp"
echo "ok $n added line(s) with ftp"
step "Rollback to revision 1 creates revision 4"
rev="$(api rollback 1)"
[[ "$rev" == 4 ]] || fail "rollback created revision $rev"
api wait-agent dns 4
expect "" dig_short mail.e2e.test A
expect "" dig_short ftp.e2e.test A
expect "" dig_short -x 192.0.2.20
expect "192.0.2.10" dig_short www.e2e.test A
expect "4" soa_serial
step "Live events (server-sent events)"
rev="$(api events-revision "after rollback")"
[[ "$rev" == 5 ]] || fail "revision during the event stream is $rev"
echo "ok event stream announced revision $rev"
api wait-agent dns 5
expect "192.0.2.20" dig_short mail.e2e.test A
step "Revoking an agent disconnects it; BIND keeps serving"
api revoke dns
api wait-disconnected dns
expect "192.0.2.20" dig_short mail.e2e.test A
step "PASSED"