- tests/integration/conftest.py: get_live_service_vips() now reads from the
config API's service_ips field instead of docker exec. The docker exec approach
spawns a fresh Python process that imports firewall_manager with its hardcoded
initial SERVICE_IPS, ignoring any update_service_ips() calls made at runtime.
The config API always computes VIPs from the current ip_range, so it matches what
the running app actually uses when writing iptables rules.
- api/app.py: reject ip_range values without a CIDR prefix (e.g. '10.0.0.1')
with a 400. Bare IPs are parsed as /32 by ipaddress.ip_network(strict=False),
which shifts all VIP offsets and produces unusable Docker subnet configs.
- tests/integration/test_config_api.py: update bare-ip test to expect 400 now
that the API enforces the prefix requirement.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>