namspill

package module
v0.1.6 Latest Latest
Warning

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

Go to latest
Published: Oct 26, 2023 License: Apache-2.0 Imports: 6 Imported by: 0

README

namspill

PkgGoDev GitHub Go Report Card Coverage

ℹ️ If you don't switch Linux-kernel namespaces and don't use a Go module that does, you most probably don't need to fret about namspill.

In the spirit of leak tests for goroutines and file descriptors, namspill tests for Linux kernel namespaces unintendedly "leaking" between goroutines due to incorrect OS thread locking (or rather, the lack thereof) when switching namespaces in a Go program.

namspill is primarily designed to integrate smoothly with the Ginkgo BDD testing framework and the Gomega matcher/assertion library. (It may be used also outside Ginkgo/Gomega, but such usage is out of scope.)

Install

go get github.com/thediveo/namspill

Usage

In its simplest form, just after each test gather information about the namespaces of the tasks (threads) of the program and check that no namespaces have leaked, with some tasks being attached to other namespaces than the rest of the tasks.

As namspill exports only very few symbols and is intended to extend the Gomega DSL, you might want to dot-import it.

import . "github.com/thediveo/namspill"

AfterEach(func() {
    // You might want to use Eventually(Tasks)... in case you don't
    // have a preceeding Eventually(Goroutines)... that waits for
    // the Goroutines (and thus Linux threads/tasks) to settle first.
    Expect(Tasks()).To(BeUniformlyNamespaced())
})

Normally, you shouldn't then see anything unless there is a problem with threads attached to other Linux-kernel namespaces when they shouldn't. In this case, the BeUniformlyNamespaced matcher will fail and show you the list of tasks with the namespaces they're attached to.

Unfortunately, there's no way to show you which goroutine might have caused this nor the call site (and more importantly, the call stack) where the namespace switch happened.

Background

namspill mostly serves as a canary to (quickly) detect forgetting to switch back namespaces before unlocking OS threads so that they can be freely scheduled to any arbitrary goroutine.

The Initial Thread M0 Is Special

Additionally, namespill checks safeguard against alternatively not terminating thread-locked goroutines. And that is where there's a catch when it comes to the Go scheduler: if a Go program's initial thread ("M0" in Go scheduler parlance) ends up being locked to a (non-main) goroutine and this goroutine exits ... then the initial thread doesn't get terminated, because that usually has unwanted side-effects on several operating systems. Instead, the Go scheduler "wedges" the initial thread and never schedules any goroutine to it again.

This situation will (correctly) trigger failed namspill assertions. To avoid the initial thread getting wedged, simply lock it in an init function to the initial/main goroutine, so it never ends up getting scheduled on any other goroutine (that might be subjected to locking it to a thread and then terminating it while being locked):

func init() {
    runtime.LockOSThread()
}

References

For further background information, please see the following references:

Make Targets

  • make: lists all targets.
  • make coverage: runs all tests with coverage and then updates the coverage badge in README.md.
  • make pkgsite: installs x/pkgsite, as well as the browser-sync and nodemon npm packages first, if not already done so. Then runs the pkgsite and hot reloads it whenever the documentation changes.
  • make report: installs @gojp/goreportcard if not yet done so and then runs it on the code base.
  • make test: runs all tests.

namspill is Copyright 2022, 2022 Harald Albrecht, and licensed under the Apache License, Version 2.0.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BeUniformlyNamespaced

func BeUniformlyNamespaced() types.GomegaMatcher

BeUniformlyNamespaced succeeds if the actual value is a slice of Task elements and all tasks share the same namespaces (per namespace type).

Types

type Task

type Task = task.Task

Task represents an individual Linux task (thread) of the running Go process; it is a type alias for task.Task.

func Tasks

func Tasks() []Task

Tasks returns information about the tasks currently belonging to this process. If the tasks cannot properly be determined, Tasks returns nil instead.

This is a re-export of the task.Tasks discovery function for convenience.

Directories

Path Synopsis
Package task discovers the Linux tasks (or, threads) of the current process.
Package task discovers the Linux tasks (or, threads) of the current process.

Jump to

Keyboard shortcuts

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