# Cases for ./granter-login.nix. Each case sets the stub's answers, runs the # rendered login under `set -e` as a unit's script does, then compares its exit # status, what it printed to stderr and the token it left exported to children. set -uo pipefail failures=0 count=0 fail() { echo "FAILED: $case: $*" >&2 failures=$((failures + 1)) } # setup setup() { case=$1 count=$((count + 1)) dir=$(mktemp -d) export FAKE_BAO_LOG=$dir/bao.log FAKE_BAO_COUNT=$dir/count FAKE_BAO_LOGIN=ok FAKE_BAO_STATUS_RC=0 unset FAKE_BAO_LOGIN_FIRST : >"$FAKE_BAO_LOG" login=$LOGIN } # run — the login, then the token it exported. Sets $rc, $got, and $dir/err. run() { got=$( ( set -e PATH="$FAKE_BAO_BIN:$PATH" unset BAO_TOKEN # shellcheck source=/dev/null . "$login" env | sed -n "s/^BAO_TOKEN=//p" ) 2>"$dir/err" ) rc=$? } # expect expect() { if [ "$rc" != "$1" ]; then fail "exited $rc, wanted $1: $(cat "$dir/err")"; fi if [ "$got" != "$2" ]; then fail "token '$got', wanted '$2'"; fi } err_has() { if ! grep -qF -- "$1" "$dir/err"; then fail "stderr lacks '$1': $(cat "$dir/err")"; fi } err_lacks() { if grep -qF -- "$1" "$dir/err"; then fail "stderr has '$1': $(cat "$dir/err")"; fi } setup "a successful login exports the token and prints nothing" run expect 0 s.fake if [ -s "$dir/err" ]; then fail "printed: $(cat "$dir/err")"; fi setup "a standby store fails the login with its own error, does not blame the role, and the next attempt succeeds" export FAKE_BAO_LOGIN_FIRST=standby run expect 1 "" err_has "local node not active but active cluster node not found" err_has "login failed without a refused certificate" err_lacks "role is not set up" err_lacks "one-time step" run expect 0 s.fake setup "an unknown role names the role and the setup steps, then bao's error" export FAKE_BAO_LOGIN=no-role run expect 1 "" err_has "refused the granter's certificate (CN bao-granter): the bao-granter role is not set up." err_has "one-time step, as root on this host" err_has " bao policy write bao-bootstrap /etc/bootstrap.hcl" err_has "invalid certificate or no client certificate supplied" err_lacks "swarm-bao-granter-role writes that role" setup "a certificate no role accepts is a missing role too" export FAKE_BAO_LOGIN=no-chain-match run expect 1 "" err_has "the bao-granter role is not set up." err_has "no chain matching all constraints" setup "a unit with a bootstrap token also points at the unit that writes the role" export FAKE_BAO_LOGIN=no-role login=$LOGIN_WITH_TOKEN run expect 1 "" err_has "swarm-bao-granter-role writes that role" setup "a sealed store is reported as sealed or unreachable, with bao's error" export FAKE_BAO_LOGIN=sealed FAKE_BAO_STATUS_RC=2 run expect 1 "" err_has "the store is sealed or unreachable; retrying." err_has "Vault is sealed" err_lacks "role is not set up" # The bootstrap token's `auth list` step: the rendered snippet runs under # `set -e` with the scratch file the unit makes. Sets $rc, $got (the listing it # left in `mounted`) and $dir/err. token=/tmp/granter-bootstrap-test.token setup_list() { setup "$1" export FAKE_BAO_LIST_COUNT=$dir/list-count FAKE_BAO_LIST=ok unset FAKE_BAO_LIST_FIRST echo dead >"$token" } run_list() { got=$( ( set -e PATH="$FAKE_BAO_BIN:$PATH" err=$(mktemp) # shellcheck source=/dev/null . "$BOOTSTRAP_LIST" echo "$mounted" ) 2>"$dir/err" ) rc=$? } setup_list "a listing that succeeds leaves it in mounted and prints nothing" run_list expect 0 '{"cert/":{},"oidc/":{}}' if [ -s "$dir/err" ]; then fail "printed: $(cat "$dir/err")"; fi setup_list "a standby store fails the listing with its own error, does not blame the token, keeps the token file, and the next attempt succeeds" export FAKE_BAO_LIST_FIRST=standby run_list expect 1 "" err_has "local node not active but active cluster node not found" err_has "listing its auth mounts failed without a refused token" err_lacks "refused the bootstrap token" err_lacks "one-time step" if [ ! -e "$token" ]; then fail "token file deleted"; fi run_list expect 0 '{"cert/":{},"oidc/":{}}' setup_list "a refused token with no working granter names the token and the setup steps, then bao's error, and exits 4" export FAKE_BAO_LIST=denied FAKE_BAO_LOGIN=no-role run_list expect 4 "" err_has "the store refused the bootstrap token in /tmp/granter-bootstrap-test.token" err_has "one-time step, as root on this host" err_has " bao policy write bao-bootstrap /etc/bootstrap.hcl" err_has "permission denied" if [ ! -e "$token" ]; then fail "token file deleted"; fi setup_list "a refused token beside a working granter exits 0 and deletes the token" export FAKE_BAO_LIST=denied FAKE_BAO_LOGIN=ok run_list expect 0 "granter role already in place; bootstrap token not needed" err_lacks "refused the bootstrap token" if [ -e "$token" ]; then fail "token file kept"; fi setup_list "a sealed store is reported as sealed or unreachable, with bao's error" export FAKE_BAO_LIST=sealed FAKE_BAO_STATUS_RC=2 run_list expect 1 "" err_has "the store is sealed or unreachable; retrying." err_has "Vault is sealed" err_lacks "refused the bootstrap token" rm -f "$token" echo "granter-login: $count cases, $failures failures" [ "$failures" = 0 ]