Newer
Older
openstack-caracal-dc-dc / tests / netem-link / run-tests.sh
#!/usr/bin/env bash
# tests/netem-link/run-tests.sh -- guard for opentofu/modules/netem-link
# (D-100 netem mechanism; local-mode amendment 2026-07-21 per D-128: the
# outer root runs ON vcloud, so empty vcloud_host_ssh_target = bare local
# `sudo tc`, non-empty = the original Office1-era SSH hop). Static shape
# assertions + a tofu-validate fixture when tofu is available.
# Exit: 0 all pass | 1 any failed. ASCII + LF.
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MOD="$(cd "$HERE/../.." && pwd)/opentofu/modules/netem-link"
FRAG="$(cd "$HERE/../.." && pwd)/scripts/sudoers.d/netem-tc"
PASS=0; FAIL=0
ok(){ echo "  PASS  $1"; PASS=$((PASS+1)); }
no(){ echo "  FAIL  $1"; FAIL=$((FAIL+1)); }

# T1: module files present
for f in main.tf variables.tf outputs.tf; do
  [ -f "$MOD/$f" ] || { no "T1 module files present ($f missing)"; echo; echo "netem-link: $PASS passed, $((FAIL)) failed"; exit 1; }
done
ok "T1 module files present"

# T2: terraform_data, not null_resource (OpenTofu's own recommendation)
grep -q 'resource "terraform_data" "netem"' "$MOD/main.tf" \
  && ! grep -v '^ *#' "$MOD/main.tf" | grep -q 'null_resource' \
  && ok "T2 terraform_data used (no null_resource)" \
  || no "T2 terraform_data used (no null_resource)"

# T3: re-apply triggers on bridge + args
grep -q 'triggers_replace = \[var.bridge_name, var.netem_args\]' "$MOD/main.tf" \
  && ok "T3 triggers_replace covers bridge_name + netem_args" \
  || no "T3 triggers_replace covers bridge_name + netem_args"

# T4: destroy-time values stashed in input (DOCFIX-194 pattern)
grep -q 'ssh_target *= *var.vcloud_host_ssh_target' "$MOD/main.tf" \
  && grep -q 'bridge_name *= *var.bridge_name' "$MOD/main.tf" \
  && ok "T4 input stash present for destroy provisioner" \
  || no "T4 input stash present for destroy provisioner"

# T5: destroy provisioner references ONLY self.* (var.* there is an INIT-time
# error -- the DOCFIX-194 class); assert no var. between 'when = destroy' and
# the end of that provisioner block
DESTROY_BLOCK="$(awk '/when *= *destroy/,/^  }/' "$MOD/main.tf")"
[ -n "$DESTROY_BLOCK" ] && ! grep -q 'var\.' <<<"$DESTROY_BLOCK" \
  && ok "T5 destroy provisioner references self.* only" \
  || no "T5 destroy provisioner references self.* only"

# T6: local mode -- empty target runs bare sudo tc in BOTH provisioners
grep -q 'var.vcloud_host_ssh_target != "" ?' "$MOD/main.tf" \
  && grep -q 'self.input.ssh_target != "" ?' "$MOD/main.tf" \
  && ok "T6 empty-target conditional present in apply + destroy" \
  || no "T6 empty-target conditional present in apply + destroy"

# T7: ssh mode retained (non-empty target still hops)
grep -q "ssh \${var.vcloud_host_ssh_target} 'sudo tc qdisc replace" "$MOD/main.tf" \
  && grep -q "ssh \${self.input.ssh_target} 'sudo tc qdisc del" "$MOD/main.tf" \
  && ok "T7 ssh hop retained for non-empty target" \
  || no "T7 ssh hop retained for non-empty target"

# T8: vcloud_host_ssh_target defaults to "" (local mode is the default)
awk '/variable "vcloud_host_ssh_target"/,/^}/' "$MOD/variables.tf" | grep -q 'default *= *""' \
  && ok "T8 vcloud_host_ssh_target default is empty (local)" \
  || no "T8 vcloud_host_ssh_target default is empty (local)"

# T9: idempotent verbs -- replace on apply, del || true on destroy
grep -q 'tc qdisc replace dev' "$MOD/main.tf" && grep -q "tc qdisc del dev" "$MOD/main.tf" \
  && grep -q '|| true' "$MOD/main.tf" \
  && ok "T9 replace on apply, del||true on destroy" \
  || no "T9 replace on apply, del||true on destroy"

# T10: no hardcoded virbrN in the module (bridge is a measured input)
grep -q 'virbr[0-9]' "$MOD"/*.tf \
  && no "T10 no virbrN literal in module sources" \
  || ok "T10 no virbrN literal in module sources"

# T11: module verbs stay inside the sudoers fragment's granted shapes
if [ -f "$FRAG" ]; then
  grep -q 'qdisc replace dev virbr[0-9]* root netem \*' "$FRAG" \
    && grep -q 'qdisc del dev virbr[0-9]* root$' "$FRAG" \
    && ok "T11 module verbs covered by netem-tc fragment shapes" \
    || no "T11 module verbs covered by netem-tc fragment shapes"
else
  echo "  SKIP  T11 sudoers fragment not present"
fi

# T12: tofu validate on a throwaway fixture instantiating BOTH modes
if command -v tofu >/dev/null 2>&1; then
  TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
  cat >"$TMP/main.tf" <<EOF
module "local_mode" {
  source      = "$MOD"
  link_name   = "t-local"
  bridge_name = "tbr0"
  netem_args  = "delay 1ms"
}
module "ssh_mode" {
  source                 = "$MOD"
  link_name              = "t-ssh"
  bridge_name            = "tbr1"
  vcloud_host_ssh_target = "user@host"
  netem_args             = "delay 1ms"
}
EOF
  if (cd "$TMP" && tofu init -backend=false >/dev/null 2>&1 && tofu validate >/dev/null 2>&1); then
    ok "T12 tofu validate passes (both modes instantiated)"
  else
    no "T12 tofu validate passes (both modes instantiated)"
  fi
else
  echo "  SKIP  T12 tofu not available"
fi

echo; echo "netem-link: $PASS passed, $FAIL failed"
[ "$FAIL" -eq 0 ]