cri-o/vendor/github.com/containernetworking/cni/pkg/ns
Nalin Dahyabhai caee4a99c9 Vendor containers/image and containers/storage
Vendor updated containers/image and containers/storage, along
with any new dependencies they drag in, and updated versions of other
dependencies that happen to get pulled in.

github.com/coreos/go-systemd/daemon/SdNotify() now takes a boolean to
control whether or not it unsets the NOTIFY_SOCKET variable from the
calling process's environment.  Adapt.

github.com/opencontainers/runtime-tools/generate/Generator.AddProcessEnv()
now takes the environment variable name and value as two arguments, not
one.  Adapt.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2017-01-18 10:21:59 -05:00
..
ns.go Vendor containers/image and containers/storage 2017-01-18 10:21:59 -05:00
README.md Build and install from GOPATH 2017-01-17 12:09:09 -08:00

Namespaces, Threads, and Go

On Linux each OS thread can have a different network namespace. Go's thread scheduling model switches goroutines between OS threads based on OS thread load and whether the goroutine would block other goroutines. This can result in a goroutine switching network namespaces without notice and lead to errors in your code.

Namespace Switching

Switching namespaces with the ns.Set() method is not recommended without additional strategies to prevent unexpected namespace changes when your goroutines switch OS threads.

Go provides the runtime.LockOSThread() function to ensure a specific goroutine executes on its current OS thread and prevents any other goroutine from running in that thread until the locked one exits. Careful usage of LockOSThread() and goroutines can provide good control over which network namespace a given goroutine executes in.

For example, you cannot rely on the ns.Set() namespace being the current namespace after the Set() call unless you do two things. First, the goroutine calling Set() must have previously called LockOSThread(). Second, you must ensure runtime.UnlockOSThread() is not called somewhere in-between. You also cannot rely on the initial network namespace remaining the current network namespace if any other code in your program switches namespaces, unless you have already called LockOSThread() in that goroutine. Note that LockOSThread() prevents the Go scheduler from optimally scheduling goroutines for best performance, so LockOSThread() should only be used in small, isolated goroutines that release the lock quickly.

The ns.Do() method provides control over network namespaces for you by implementing these strategies. All code dependent on a particular network namespace (including the root namespace) should be wrapped in the ns.Do() method to ensure the correct namespace is selected for the duration of your code. For example:

targetNs, err := ns.NewNS()
if err != nil {
    return err
}
err = targetNs.Do(func(hostNs ns.NetNS) error {
	dummy := &netlink.Dummy{
		LinkAttrs: netlink.LinkAttrs{
			Name: "dummy0",
		},
	}
	return netlink.LinkAdd(dummy)
})

Note this requirement to wrap every network call is very onerous - any libraries you call might call out to network services such as DNS, and all such calls need to be protected after you call ns.Do(). The CNI plugins all exit very soon after calling ns.Do() which helps to minimize the problem.

Further Reading