diff --git a/daemon/libnetwork/drivers/bridge/internal/iptabler/iptabler_test.go b/daemon/libnetwork/drivers/bridge/internal/iptabler/iptabler_test.go index 30c826f3b3..e392d90891 100644 --- a/daemon/libnetwork/drivers/bridge/internal/iptabler/iptabler_test.go +++ b/daemon/libnetwork/drivers/bridge/internal/iptabler/iptabler_test.go @@ -149,7 +149,7 @@ func TestIptabler(t *testing.T) { } func testIptabler(t *testing.T, tn string, config firewaller.Config, netConfig firewaller.NetworkConfig, bindLocalhost bool, resName string) { - defer netnsutils.SetupTestOSContext(t)() + defer netnsutils.SetupTestOSContext(t, netnsutils.WithSetNsHandles(false))() stripComments := func(text string) string { lines := strings.Split(text, "\n") diff --git a/daemon/libnetwork/drivers/bridge/internal/nftabler/nftabler_test.go b/daemon/libnetwork/drivers/bridge/internal/nftabler/nftabler_test.go index af96a853d5..d00d5f99c4 100644 --- a/daemon/libnetwork/drivers/bridge/internal/nftabler/nftabler_test.go +++ b/daemon/libnetwork/drivers/bridge/internal/nftabler/nftabler_test.go @@ -91,7 +91,7 @@ func TestNftabler(t *testing.T) { } func testNftabler(t *testing.T, tn string, config firewaller.Config, netConfig firewaller.NetworkConfig, bindLocalhost bool, resName string) { - defer netnsutils.SetupTestOSContext(t)() + defer netnsutils.SetupTestOSContext(t, netnsutils.WithSetNsHandles(false))() checkResults := func(family, name string, en bool) { t.Helper() diff --git a/internal/testutil/netnsutils/context_unix.go b/internal/testutil/netnsutils/context_unix.go index e70bf4ce08..1bd5c2a3b4 100644 --- a/internal/testutil/netnsutils/context_unix.go +++ b/internal/testutil/netnsutils/context_unix.go @@ -25,8 +25,22 @@ var osContextLock sync.Mutex type OSContext struct { origNS, newNS netns.NsHandle - tid int - caller string // The file:line where SetupTestOSContextEx was called, for interpolating into error messages. + tid int + caller string // The file:line where SetupTestOSContextEx was called, for interpolating into error messages. + setNsHandles bool // See [WithSetNsHandles]. +} + +// WithSetNsHandles is an option for [SetupTestOSContext]/[SetupTestOSContextEx], +// value false prevents reset of the namespace and netlink handles in package ns. +// This allows multiple OSContext instances to be active at the same time, +// but means code that relies on package ns, including [ns.NlHandle], will +// not work correctly. So, it's useful for tests that want clean network +// namespaces for parallel subtests, as long as they don't use package ns. +// The default is true. +func WithSetNsHandles(p bool) func(*OSContext) { + return func(c *OSContext) { + c.setNsHandles = p + } } // SetupTestOSContext joins the current goroutine to a new network namespace, @@ -35,8 +49,8 @@ type OSContext struct { // Example usage: // // defer SetupTestOSContext(t)() -func SetupTestOSContext(t *testing.T) func() { - c := SetupTestOSContextEx(t) +func SetupTestOSContext(t *testing.T, opts ...func(*OSContext)) func() { + c := SetupTestOSContextEx(t, opts...) return func() { c.Cleanup(t) } } @@ -50,8 +64,19 @@ func SetupTestOSContext(t *testing.T) func() { // // c := SetupTestOSContext(t) // defer c.Cleanup(t) -func SetupTestOSContextEx(t *testing.T) *OSContext { - osContextLock.Lock() +func SetupTestOSContextEx(t *testing.T, opts ...func(*OSContext)) *OSContext { + c := OSContext{ + setNsHandles: true, + } + for _, o := range opts { + o(&c) + } + // If the namespace/netlink handles in package ns are to be reset, make sure + // only one OSContext is active at a time. + if c.setNsHandles { + osContextLock.Lock() + } + runtime.LockOSThread() origNS, err := netns.Get() if err != nil { @@ -59,10 +84,8 @@ func SetupTestOSContextEx(t *testing.T) *OSContext { t.Fatalf("Failed to open initial netns: %v", err) } - c := OSContext{ - tid: unix.Gettid(), - origNS: origNS, - } + c.tid = unix.Gettid() + c.origNS = origNS c.newNS, err = netns.New() if err != nil { // netns.New() is not atomic: it could have encountered an error @@ -73,7 +96,9 @@ func SetupTestOSContextEx(t *testing.T) *OSContext { // Since we are switching to a new test namespace make // sure to re-initialize initNs context - ns.ResetHandles() + if c.setNsHandles { + ns.ResetHandles() + } nl := ns.NlHandle() lo, err := nl.LinkByName("lo") @@ -108,8 +133,10 @@ func (c *OSContext) Cleanup(t *testing.T) { t.Logf("Warning: netns closing failed (%v)", err) } c.restore(t) - ns.ResetHandles() - osContextLock.Unlock() + if c.setNsHandles { + ns.ResetHandles() + osContextLock.Unlock() + } } func (c *OSContext) restore(t *testing.T) {