mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
Reset default bridge addresses after integration tests
CI runs a docker daemon, and some tests run their own. That tramples the CI daemon's default bridge. If a test leaves the bridge with only a link-local IPv6 address (for example), subsequent tests are likely to fail - even if they also start their own daemon because, by default the default bridge network's config is based on an existing bridge device. So, remember the bridge's addresses at the start of each test, and restore them afterwards. Signed-off-by: Rob Murray <rob.murray@docker.com>
This commit is contained in:
@@ -39,6 +39,7 @@ func (e *Execution) Clean(ctx context.Context, t testing.TB) {
|
||||
deleteAllNetworks(ctx, t, apiClient, platform, e.protectedElements.networks)
|
||||
if platform == "linux" {
|
||||
deleteAllPlugins(ctx, t, apiClient, e.protectedElements.plugins)
|
||||
restoreDefaultBridge(t, e.protectedElements.defaultBridgeInfo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,11 +25,12 @@ var frozenImages = []string{
|
||||
}
|
||||
|
||||
type protectedElements struct {
|
||||
containers map[string]struct{}
|
||||
images map[string]struct{}
|
||||
networks map[string]struct{}
|
||||
plugins map[string]struct{}
|
||||
volumes map[string]struct{}
|
||||
containers map[string]struct{}
|
||||
defaultBridgeInfo *defaultBridgeInfo
|
||||
images map[string]struct{}
|
||||
networks map[string]struct{}
|
||||
plugins map[string]struct{}
|
||||
volumes map[string]struct{}
|
||||
}
|
||||
|
||||
func newProtectedElements() protectedElements {
|
||||
@@ -57,6 +58,7 @@ func ProtectAll(ctx context.Context, t testing.TB, testEnv *Execution) {
|
||||
ProtectNetworks(ctx, t, testEnv)
|
||||
ProtectVolumes(ctx, t, testEnv)
|
||||
if testEnv.DaemonInfo.OSType == "linux" {
|
||||
ProtectDefaultBridge(ctx, t, testEnv)
|
||||
ProtectPlugins(ctx, t, testEnv)
|
||||
}
|
||||
}
|
||||
|
||||
106
testutil/environment/protect_linux.go
Normal file
106
testutil/environment/protect_linux.go
Normal file
@@ -0,0 +1,106 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.22
|
||||
|
||||
package environment
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"maps"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/internal/nlwrap"
|
||||
"github.com/docker/docker/libnetwork/drivers/bridge"
|
||||
"github.com/vishvananda/netlink"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
type defaultBridgeInfo struct {
|
||||
bridge netlink.Link
|
||||
addrs map[string]*netlink.Addr
|
||||
}
|
||||
|
||||
var _, llSubnet, _ = net.ParseCIDR("fe80::/64")
|
||||
|
||||
// ProtectDefaultBridge remembers default bridge settings so that, when a test
|
||||
// runs its own daemon and tramples settings of the bridge belonging to the
|
||||
// CI-started bridge, the bridge is restored to its old state before the next
|
||||
// test.
|
||||
//
|
||||
// For example, a test may enable IPv6 with a link-local fixed-cidr-v6. That's
|
||||
// likely to break later tests, even if they also start their own daemon
|
||||
// (because, in the absence of any specific settings, the daemon learns default
|
||||
// bridge config from addresses on an existing bridge device).
|
||||
func ProtectDefaultBridge(_ context.Context, t testing.TB, testEnv *Execution) {
|
||||
t.Helper()
|
||||
// Find the bridge - there should always be one, belonging to the daemon started by CI.
|
||||
br, err := nlwrap.LinkByName(bridge.DefaultBridgeName)
|
||||
if err != nil {
|
||||
var lnf netlink.LinkNotFoundError
|
||||
if !errors.As(err, &lnf) {
|
||||
t.Fatal("Getting default bridge before test:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
testEnv.ProtectDefaultBridge(t, &defaultBridgeInfo{
|
||||
bridge: br,
|
||||
addrs: getAddrs(t, br),
|
||||
})
|
||||
}
|
||||
|
||||
func getAddrs(t testing.TB, br netlink.Link) map[string]*netlink.Addr {
|
||||
t.Helper()
|
||||
addrs, err := nlwrap.AddrList(br, netlink.FAMILY_ALL)
|
||||
assert.NilError(t, err, "Getting default bridge addresses before test")
|
||||
addrMap := map[string]*netlink.Addr{}
|
||||
for _, addr := range addrs {
|
||||
addrMap[addr.IPNet.String()] = &addr
|
||||
}
|
||||
return addrMap
|
||||
}
|
||||
|
||||
// ProtectDefaultBridge stores default bridge info, to be restored on clean.
|
||||
func (e *Execution) ProtectDefaultBridge(t testing.TB, info *defaultBridgeInfo) {
|
||||
e.protectedElements.defaultBridgeInfo = info
|
||||
}
|
||||
|
||||
func restoreDefaultBridge(t testing.TB, info *defaultBridgeInfo) {
|
||||
t.Helper()
|
||||
if info == nil {
|
||||
return
|
||||
}
|
||||
// Re-create the bridge if the test was antisocial enough to delete it.
|
||||
// Yes, I'm looking at you TestDockerDaemonSuite/TestBuildOnDisabledBridgeNetworkDaemon.
|
||||
br, err := nlwrap.LinkByName(bridge.DefaultBridgeName)
|
||||
if err != nil {
|
||||
var lnf netlink.LinkNotFoundError
|
||||
if !errors.As(err, &lnf) {
|
||||
t.Fatal("Failed to find default bridge after test:", err)
|
||||
}
|
||||
err := netlink.LinkAdd(info.bridge)
|
||||
assert.NilError(t, err, "Failed to re-create default bridge after test")
|
||||
br, err = nlwrap.LinkByName(bridge.DefaultBridgeName)
|
||||
assert.NilError(t, err, "Failed to find re-created default bridge after test")
|
||||
}
|
||||
addrs, err := nlwrap.AddrList(br, netlink.FAMILY_ALL)
|
||||
assert.NilError(t, err, "Failed get default bridge addresses after test")
|
||||
// Delete addresses the bridge didn't have before the test, apart from IPv6 LL
|
||||
// addresses - because the bridge doesn't get a kernel-assigned LL address until
|
||||
// the first veth is hooked up and, once that address is deleted, it's not
|
||||
// re-added.
|
||||
wantAddrs := maps.Clone(info.addrs)
|
||||
for _, addr := range addrs {
|
||||
if _, ok := wantAddrs[addr.IPNet.String()]; ok {
|
||||
delete(wantAddrs, addr.IPNet.String())
|
||||
} else if !llSubnet.Contains(addr.IP) {
|
||||
err := netlink.AddrDel(br, &netlink.Addr{IPNet: addr.IPNet})
|
||||
assert.NilError(t, err, "Failed to remove default bridge address '%s' after test", addr.IPNet.String())
|
||||
}
|
||||
}
|
||||
// Add missing addresses.
|
||||
for _, wantAddr := range wantAddrs {
|
||||
err = netlink.AddrAdd(br, wantAddr)
|
||||
assert.NilError(t, err, "Failed to add default bridge address '%s' after test", wantAddr.IPNet.String())
|
||||
}
|
||||
}
|
||||
16
testutil/environment/protect_others.go
Normal file
16
testutil/environment/protect_others.go
Normal file
@@ -0,0 +1,16 @@
|
||||
//go:build !linux
|
||||
|
||||
package environment
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type defaultBridgeInfo struct{}
|
||||
|
||||
func ProtectDefaultBridge(context.Context, testing.TB, *Execution) {
|
||||
return
|
||||
}
|
||||
|
||||
func restoreDefaultBridge(testing.TB, *defaultBridgeInfo) {}
|
||||
Reference in New Issue
Block a user