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>
176 lines
5.8 KiB
Bash
Executable File
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"
|