fix(get_config): strip veth interfaces — Docker container churn causes false-positive config changes every poll
Docker creates a fresh veth pair with a new random name and ifindex for every container start/restart. ip addr show includes them, so get_config() reported a "config change" on nearly every poll of a Docker host even though nothing about the host's own configuration changed.
This commit is contained in:
@@ -697,13 +697,19 @@ class LinuxDriver(OSDriver):
|
|||||||
|
|
||||||
``valid_lft`` / ``preferred_lft`` fields from DHCP leases are stripped
|
``valid_lft`` / ``preferred_lft`` fields from DHCP leases are stripped
|
||||||
so the output is stable across polls and does not produce false-positive
|
so the output is stable across polls and does not produce false-positive
|
||||||
config-change events in the config-backup feature.
|
config-change events in the config-backup feature. ``veth*`` interfaces
|
||||||
|
are stripped entirely — Docker creates/destroys them with a fresh index
|
||||||
|
and random name on every container restart, which would otherwise flag
|
||||||
|
a config change on nearly every poll of a Docker host.
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
|
|
||||||
running = self._send("ip addr show && ip route show")
|
running = self._send("ip addr show && ip route show")
|
||||||
# Strip volatile DHCP lease timer fields — they decrement every poll
|
# Strip volatile DHCP lease timer fields — they decrement every poll
|
||||||
running = re.sub(r"\s+valid_lft\s+\S+\s+preferred_lft\s+\S+", "", running)
|
running = re.sub(r"\s+valid_lft\s+\S+\s+preferred_lft\s+\S+", "", running)
|
||||||
|
# Strip veth interface blocks (line + indented sub-lines) — ephemeral
|
||||||
|
# Docker container network endpoints, not intentional host config
|
||||||
|
running = re.sub(r"^\d+: veth\S*:.*\n(?:[ \t]+.*\n?)*", "", running, flags=re.MULTILINE)
|
||||||
return {"running": running, "startup": "", "candidate": ""}
|
return {"running": running, "startup": "", "candidate": ""}
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|||||||
@@ -88,6 +88,46 @@ def test_get_interfaces_parses_state(driver):
|
|||||||
assert result["eth1"]["is_up"] is False
|
assert result["eth1"]["is_up"] is False
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# get_config
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
IP_ADDR_ROUTE_WITH_VETH = """\
|
||||||
|
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
|
||||||
|
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
|
||||||
|
inet 127.0.0.1/8 scope host lo
|
||||||
|
valid_lft forever preferred_lft forever
|
||||||
|
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
|
||||||
|
link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff
|
||||||
|
inet 192.168.1.10/24 brd 192.168.1.255 scope global dynamic eth0
|
||||||
|
valid_lft 3542sec preferred_lft 3542sec
|
||||||
|
3512: veth5d7e34d@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-ebb930396f3b state UP group default
|
||||||
|
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
|
||||||
|
default via 192.168.1.1 dev eth0
|
||||||
|
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.10
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_config_strips_dhcp_lease_timers(driver):
|
||||||
|
with patch.object(driver, "_send", return_value=IP_ADDR_ROUTE_WITH_VETH):
|
||||||
|
result = driver.get_config()
|
||||||
|
assert "valid_lft" not in result["running"]
|
||||||
|
assert "preferred_lft" not in result["running"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_config_strips_veth_interfaces(driver):
|
||||||
|
"""Docker creates/destroys veth pairs on every container restart — including
|
||||||
|
them would make get_config() report a false-positive change on every poll."""
|
||||||
|
with patch.object(driver, "_send", return_value=IP_ADDR_ROUTE_WITH_VETH):
|
||||||
|
result = driver.get_config()
|
||||||
|
assert "veth5d7e34d" not in result["running"]
|
||||||
|
assert "3512:" not in result["running"]
|
||||||
|
# Real interfaces and routes must survive the filter
|
||||||
|
assert "eth0" in result["running"]
|
||||||
|
assert "default via 192.168.1.1" in result["running"]
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# _parse_uptime
|
# _parse_uptime
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user