This avoids depending on global state in the package, and "forces"
consumers to go through the initHandles() func to get the handles.
A slight change in behavior is that `ResetHandles()` may now initialize
a new namespace only to reset it, but this is likely an "OK" trade-off.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
| ... | ... |
@@ -14,48 +14,55 @@ import ( |
| 14 | 14 |
"github.com/vishvananda/netns" |
| 15 | 15 |
) |
| 16 | 16 |
|
| 17 |
-var ( |
|
| 18 |
- initNs = netns.None() |
|
| 19 |
- initNl nlwrap.Handle |
|
| 20 |
- initOnce sync.Once |
|
| 21 |
- // NetlinkSocketsTimeout represents the default timeout duration for the sockets |
|
| 22 |
- NetlinkSocketsTimeout = 3 * time.Second |
|
| 23 |
-) |
|
| 17 |
+// NetlinkSocketsTimeout represents the default timeout duration for the sockets. |
|
| 18 |
+var NetlinkSocketsTimeout = 3 * time.Second |
|
| 19 |
+ |
|
| 20 |
+// initNamespace initializes a new network namespace. |
|
| 21 |
+var initNamespace = sync.OnceValues(initHandles) |
|
| 24 | 22 |
|
| 25 | 23 |
// initHandles initializes a new network namespace |
| 26 |
-func initHandles() {
|
|
| 27 |
- var err error |
|
| 28 |
- initNs, err = netns.Get() |
|
| 24 |
+func initHandles() (netns.NsHandle, nlwrap.Handle) {
|
|
| 25 |
+ initNs, err := netns.Get() |
|
| 29 | 26 |
if err != nil {
|
| 30 |
- log.G(context.TODO()).Errorf("could not get initial namespace: %v", err)
|
|
| 27 |
+ log.G(context.Background()).WithError(err).Error("could not get initial namespace: falling back to using netns.None")
|
|
| 28 |
+ initNs = netns.None() |
|
| 31 | 29 |
} |
| 32 |
- initNl, err = nlwrap.NewHandle(getSupportedNlFamilies()...) |
|
| 30 |
+ initNl, err := nlwrap.NewHandle(getSupportedNlFamilies()...) |
|
| 33 | 31 |
if err != nil {
|
| 34 | 32 |
// Fail fast to keep the invariant: NlHandle must be a valid handle |
| 35 | 33 |
panic(fmt.Errorf("could not create netlink handle on initial (host) namespace: %w", err))
|
| 36 | 34 |
} |
| 37 | 35 |
err = initNl.SetSocketTimeout(NetlinkSocketsTimeout) |
| 38 | 36 |
if err != nil {
|
| 39 |
- log.G(context.TODO()).Warnf("Failed to set the timeout on the default netlink handle sockets: %v", err)
|
|
| 37 |
+ log.G(context.Background()).WithError(err).Warn("failed to set the timeout on the default netlink handle sockets")
|
|
| 40 | 38 |
} |
| 39 |
+ |
|
| 40 |
+ return initNs, initNl |
|
| 41 | 41 |
} |
| 42 | 42 |
|
| 43 | 43 |
// ResetHandles resets the initial namespace and netlink handles. |
| 44 | 44 |
// This is useful for testing to ensure a clean state. It will |
| 45 | 45 |
// panic if called outside a test. |
| 46 |
+// |
|
| 47 |
+// Note: This function is not safe for concurrent use with callers |
|
| 48 |
+// that are using handles obtained from this package. It may close |
|
| 49 |
+// handles while they are still in use. |
|
| 46 | 50 |
func ResetHandles() {
|
| 47 | 51 |
if !testing.Testing() {
|
| 48 | 52 |
panic("ResetHandles should only be called from tests")
|
| 49 | 53 |
} |
| 54 |
+ initNs, initNl := initNamespace() |
|
| 55 |
+ // Reset the initNamespace sync.OnceValues. This may race with |
|
| 56 |
+ // concurrent callers still calling the old initNamespace (and |
|
| 57 |
+ // values), but adding a [sync.RWMutex] only for the test-case |
|
| 58 |
+ // is probably too much (unless things are racy). |
|
| 59 |
+ initNamespace = sync.OnceValues(initHandles) |
|
| 50 | 60 |
if initNs.IsOpen() {
|
| 51 | 61 |
_ = initNs.Close() |
| 52 |
- initNs = netns.None() |
|
| 53 | 62 |
} |
| 54 | 63 |
if initNl.Handle != nil {
|
| 55 | 64 |
initNl.Close() |
| 56 |
- initNl = nlwrap.Handle{}
|
|
| 57 | 65 |
} |
| 58 |
- initOnce = sync.Once{}
|
|
| 59 | 66 |
} |
| 60 | 67 |
|
| 61 | 68 |
// ParseHandlerInt transforms the namespace handler into an integer |
| ... | ... |
@@ -65,14 +72,14 @@ func ParseHandlerInt() int {
|
| 65 | 65 |
|
| 66 | 66 |
// GetHandler returns the namespace handler |
| 67 | 67 |
func getHandler() netns.NsHandle {
|
| 68 |
- initOnce.Do(initHandles) |
|
| 69 |
- return initNs |
|
| 68 |
+ ns, _ := initNamespace() |
|
| 69 |
+ return ns |
|
| 70 | 70 |
} |
| 71 | 71 |
|
| 72 | 72 |
// NlHandle returns the netlink handler |
| 73 | 73 |
func NlHandle() nlwrap.Handle {
|
| 74 |
- initOnce.Do(initHandles) |
|
| 75 |
- return initNl |
|
| 74 |
+ _, nl := initNamespace() |
|
| 75 |
+ return nl |
|
| 76 | 76 |
} |
| 77 | 77 |
|
| 78 | 78 |
func getSupportedNlFamilies() []int {
|