mntns

package
v1.7.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 11, 2024 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package mntns supports running unit tests in separated transient mount namespaces.

Wait ... what do mount(!) namespaces have to do with virtual networks and testing?

The “/sys/class/net” branch of sysfs(5).

Some Sysfs Background

According to sysfs(5), “[e]ach of the entries in this directory is a symbolic link representing one of the real or virtual networking devices [...] Each of these symbolic links refers to entries in the /sys/devices directory.” (omission ours)

Unfortunately, the man page is wrong in the omitted part of the description, about “real or virtual networking devices that are visible in the network namespace of the process that is accessing the directory”. According to this answer to Switching into a network namespace does not change /sys/class/net? – which can be easily verified, not least in the self-test units of notwork – the sysfs locks the “sys/class/net“ view to the network namespace of the (OS-level) thread that mounted that particular sysfs instance.

Thus, unit tests working with the “sys/class/net” branch need to create and enter a transient mount namespace after they've created and entered a transient network namespace, and then also mount a new sysfs instance onto “/sys” to get a consistent view.

Usage

To create transient mount and network namespaces and mount a correct sysfs instance:

import (
	"github.com/notwork/mntns"
	"github.com/notwork/netns"
)

It("creates transient mount and network namespaces, mounts a sysfs", func() {
	defer netns.EnterTransient()()
	defer mntns.EnterTransient()()
	mntns.MountSysfsRO()
})

Or without normally entering the mount namespace:

import (
	"github.com/notwork/mntns"
	"github.com/notwork/netns"
)

It("creates transient mount and network namespaces, mounts a sysfs", func() {
	defer netns.EnterTransient()()
	mntnsfd, procfsroot := mntns.NewTransient()
	mntns.Execute(mntnsfd, func() {
		mntns.MountSysfsRO()
	}
})

Here, the returned “procfsroot” path is in the form of “/proc/$TID/root” and allows accessing directories and files in the transient mount namespace without the need to enter it. In case you need to work with absolute symbolic links, procfsroot will help by resolving absolute symbolic links inside a different mount namespace correctly; please refer to the procfsroot package for details.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Current

func Current() int

Current returns a file descriptor referencing the current mount namespace. In particular, the current mount namespace of the OS-level thread of the caller's Go routine (which should ideally be thread-locked).

When not running in the initial mount namesepace you should have the calling go routine locked to its OS-level thread.

Additionally, Current schedules a DeferCleanup of the returned file descriptor to be closed to avoid leaking it.

func EnterTransient

func EnterTransient() func()

EnterTransient creates and enters a new mount namespace, returning a function that needs to be defer'ed. It additionally remounts “/” in this new mount namespace to set propagation of mount points to “private” – otherwise, later using MountSysfsRO will end in tears as the new “/sys” mount would otherwise happily propagate back (and we absolutely don't want that to happen).

Note: the current OS-level thread won't be unlocked when the calling unit test returns, as we cannot undo unsharing filesystem attributes (using CLONE_FS) such as the root directory, current directory, and umask attributes.

Background

unshare(1) defaults the mount point propagation to "MS_REC | MS_PRIVATE", see util-linux/unshare.c UNSHARE_PROPAGATION_DEFAULT.

unshare(1) is remounting "/" in order to apply its propagation defaults -- that are to not FUBAR the mount points in the mount namespace we got our mount points from during unsharing the mount namespace, see util-linux/unshare.c set_propagation.

/* C */ mount("none", "/", NULL, flags, NULL

func Execute

func Execute(mntnsfd int, fn func())

Execute a function fn in a separate(!) Go routine in the mount namespace referenced by the open file descriptor mntnsfd. In order to avoid race issues, the calling Go routine is blocked until the called fn returns. Any results of the called fn should be communicated back from fn to the caller using a buffered(!) channel.

Execute additionally switches the fn executing Go routine to the network namespace of the caller if the caller's Go routine network namespace differs from the network namespace of the process itself.

func Ino

func Ino[R ~int | ~string](mntns R) uint64

Ino returns the identification/inode number of the passed mount namespace, either referenced by a file descriptor or a VFS path name.

func MountSysfsRO

func MountSysfsRO()

MountSysfsRO mounts a new sysfs instance read-only onto /sys when the caller is in a new and transient mount namespace. Otherwise, MountSysfsRO will fail the current test.

func NewTransient

func NewTransient() (mntfd int, procfsroot string)

NewTransient creates a new transient mount namespace that is kept alive by a an idle OS-level thread; this idle thread is automatically terminated upon returning from the current test.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL