Files
fips/testing/iface-binding/docker-compose.yml
T
Arjen b8c4a5584f test(iface-binding): cover an interface present before the daemon starts
Every scenario in the suite created its interface after the daemons were
already running — that ordering is the boot race the suite was written for.
But it means both nodes could only ever reach Present through binder_loop,
so the inline bind in start_async, which is the ordinary case on a booted
router, had no end-to-end coverage at all. That is where the churn guard
went unseeded and the first detach stopped reaching node health, and no
existing case could reach it: they all detach from a binding the loop
created, which seeds the guard as a side effect.

Case (f) adds a third node whose single required interface exists before its
daemon does. The gate is what buys that ordering — the harness needs a
running container to have a netns to move a veth into, but the daemon must
not start until after the move, so node-c comes up parked on a file and the
harness releases it once the interface is in place. Then one detach, on a
binding the loop did not create, and the node must degrade.

Verified against the defect rather than only against the fix: with the guard
seed reverted, cases (a) through (e) all still pass and (f) is the only
failure. A regression test that has never been seen to fail is a claim, not
a test.

It also asserts the reverse edge, so Degraded stays a level rather than a
latch on this path too.
2026-09-01 13:17:03 +01:00

81 lines
3.4 KiB
YAML

networks:
# Management bridge only. The FIPS transport under test is raw Ethernet on a
# veth pair the harness creates *after* the daemons are already running —
# that is the whole point of the suite — so no FIPS traffic crosses this
# network. No subnet is requested, so two concurrent runs cannot collide on
# one address range.
#
# The compose project name is still fixed, so two runs that do not set
# COMPOSE_PROJECT_NAME share a project and the second `up` recreates the
# first's containers. The local CI runner scopes it externally
# (run_iface_binding in ci-local.sh); a bare hand run does not.
ifb-net:
driver: bridge
labels:
- "com.corganlabs.fips-ci=1"
x-fips-common: &fips-common
build:
# The harness scopes its build context per run and passes it here; the
# shared directory is the hand-run default. Compose resolves a relative
# value against THIS file's directory, so the harness must export an
# absolute path.
context: ${FIPS_BUILD_CONTEXT:-../docker}
image: ${FIPS_TEST_IMAGE:-fips-test:latest}
entrypoint: ["/usr/local/bin/entrypoint.sh"]
cap_add:
- NET_ADMIN
- NET_RAW
restart: "no"
environment:
# `default`, deliberately — NOT `chaos`. The chaos entrypoint waits up to
# 30 s for every configured Ethernet interface to appear before it starts
# the daemon, which is precisely the workaround this mechanism retires. The
# daemon must do its own waiting here or the suite proves nothing.
- FIPS_TEST_MODE=default
- RUST_LOG=info,fips::transport::ethernet=debug,fips::node=debug
networks:
- ifb-net
services:
node-a:
<<: *fips-common
container_name: fips-ifb-node-a${FIPS_CI_NAME_SUFFIX:-}
hostname: host-a
volumes:
- ../docker/resolv.conf:/etc/resolv.conf:ro
- ./generated-configs${FIPS_CI_NAME_SUFFIX:-}/node-a/fips.yaml:/etc/fips/fips.yaml:ro
- ./generated-configs${FIPS_CI_NAME_SUFFIX:-}/node-a/fips.key:/etc/fips/fips.key:ro
node-b:
<<: *fips-common
container_name: fips-ifb-node-b${FIPS_CI_NAME_SUFFIX:-}
hostname: host-b
volumes:
- ../docker/resolv.conf:/etc/resolv.conf:ro
- ./generated-configs${FIPS_CI_NAME_SUFFIX:-}/node-b/fips.yaml:/etc/fips/fips.yaml:ro
- ./generated-configs${FIPS_CI_NAME_SUFFIX:-}/node-b/fips.key:/etc/fips/fips.key:ro
# The clean-start case. Its interface exists before its daemon does, which is
# the ordinary state of a booted router and the one ordering node-a and
# node-b cannot produce: their interface is created after they are already
# running, so they can only ever bind through the binder loop.
#
# The gate is what buys that ordering. The harness needs a running container
# to have a netns to move a veth into, but the daemon must not start until
# after the move — so the container comes up, parks on this file, and the
# harness releases it once the interface is in place.
node-c:
<<: *fips-common
container_name: fips-ifb-node-c${FIPS_CI_NAME_SUFFIX:-}
hostname: host-c
entrypoint: ["/bin/sh", "-c"]
command:
- |
while [ ! -e /tmp/fips-go ]; do sleep 0.2; done
exec /usr/local/bin/entrypoint.sh
volumes:
- ../docker/resolv.conf:/etc/resolv.conf:ro
- ./generated-configs${FIPS_CI_NAME_SUFFIX:-}/node-c/fips.yaml:/etc/fips/fips.yaml:ro
- ./generated-configs${FIPS_CI_NAME_SUFFIX:-}/node-c/fips.key:/etc/fips/fips.key:ro