New Upstream Release - golang-github-vishvananda-netns

Ready changes

Summary

Merged new upstream version: 0.0~git20230405.16c2fa0 (was: 0.0~git20221102.43aa913).

Resulting package

Built on 2023-05-22T10:26 (took 5m38s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases golang-github-vishvananda-netns-dev

Lintian Result

Diff

diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..7f451b7
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,11 @@
+version: 2
+updates:
+  - package-ecosystem: "github-actions"
+    directory: "/"
+    schedule:
+      interval: "monthly"
+
+  - package-ecosystem: "gomod"  # Dependencies listed in go.mod
+    directory: "/"  # Location of package manifests
+    schedule:
+      interval: "weekly"
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..7780563
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,43 @@
+name: test
+on:
+  push:
+    tags:
+      - v*
+    branches:
+      - master
+      - main
+  pull_request:
+    branches:
+      - master
+      - main
+
+jobs:
+  test:
+    permissions:
+      contents: read  # for actions/checkout to fetch code
+    timeout-minutes: 10
+
+    strategy:
+      matrix:
+        # test against the "oldest" supported version and the current version
+        # of go. Go 1.17 is kept in this matrix as it is the minimum version
+        # specified in go.mod, and maintaining compatibility with go 1.17 is
+        # currently not much of a burden. Most projects using this module are
+        # using newer versions than that, so we can drop the old version if
+        # it becomes too much of a burden.
+        go-version: [1.17.x, stable]
+        os: [ubuntu-20.04, ubuntu-22.04, windows-2022]
+    runs-on: ${{ matrix.os }}
+    steps:
+      - name: Install Go
+        uses: actions/setup-go@v4
+        with:
+          go-version: ${{ matrix.go-version }}
+      - name: Checkout code
+        uses: actions/checkout@v3
+      - name: go mod tidy
+        run: |
+          go mod tidy
+          git diff --exit-code
+      - name: Test
+        run: go test -exec "sudo -n" -v ./...
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
new file mode 100644
index 0000000..c8d3423
--- /dev/null
+++ b/.github/workflows/validate.yml
@@ -0,0 +1,54 @@
+name: validate
+on:
+  push:
+    tags:
+      - v*
+    branches:
+      - master
+      - main
+  pull_request:
+    branches:
+      - master
+      - main
+
+jobs:
+  linters:
+    permissions:
+      contents: read  # for actions/checkout to fetch code
+      pull-requests: read  # for golangci/golangci-lint-action to fetch pull requests
+    timeout-minutes: 10
+
+    strategy:
+      matrix:
+        # We only run on the latest version of go, as some linters may be
+        # version-dependent (for example gofmt can change between releases).
+        go-version: [stable]
+        os: [ubuntu-22.04, windows-2022]
+    runs-on: ${{ matrix.os }}
+    steps:
+      - name: Install Go
+        uses: actions/setup-go@v4
+        with:
+          go-version: ${{ matrix.go-version }}
+      - name: Checkout code
+        uses: actions/checkout@v3
+      - name: YAML Lint
+        if: runner.os == 'Linux'
+        uses: ibiqlik/action-yamllint@v3
+        with:
+          format: auto
+      - name: Golangci-lint
+        uses: golangci/golangci-lint-action@v3
+        with:
+          version: latest
+          skip-cache: true
+          args: --print-resources-usage --verbose
+
+          # Optional: show only new issues if it's a pull request. The default value is `false`.
+          # only-new-issues: true
+
+          # Optional: if set to true then the action don't cache or restore ~/go/pkg.
+          # skip-pkg-cache: true
+
+          # Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
+          # skip-build-cache: true
diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 0000000..2b6988f
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,26 @@
+linters:
+  enable:
+    - errcheck
+    - errorlint
+    - gocritic
+    - gosec
+    - gosimple
+    - govet
+    - gci
+    - misspell
+    - nonamedreturns
+    - staticcheck
+    - unconvert
+    - unparam
+    - unused
+    - whitespace
+
+linters-settings:
+  gci:
+    sections:
+      - standard
+      - default
+      - prefix(github.com/vishvananda)
+
+run:
+  timeout: 5m
diff --git a/.yamllint.yml b/.yamllint.yml
new file mode 100644
index 0000000..1b2830c
--- /dev/null
+++ b/.yamllint.yml
@@ -0,0 +1,9 @@
+---
+extends: default
+
+rules:
+  document-start: disable
+  line-length: disable
+  truthy:
+    ignore: |
+      .github/workflows/*.yml
diff --git a/README.md b/README.md
index 1fdb2d3..bdfedbe 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,7 @@ import (
     "fmt"
     "net"
     "runtime"
+
     "github.com/vishvananda/netns"
 )
 
@@ -48,14 +49,3 @@ func main() {
 }
 
 ```
-
-## NOTE
-
-The library can be safely used only with Go >= 1.10 due to [golang/go#20676](https://github.com/golang/go/issues/20676).
-
-After locking a goroutine to its current OS thread with `runtime.LockOSThread()`
-and changing its network namespace, any new subsequent goroutine won't be
-scheduled on that thread while it's locked. Therefore, the new goroutine
-will run in a different namespace leading to unexpected results.
-
-See [here](https://www.weave.works/blog/linux-namespaces-golang-followup) for more details.
diff --git a/debian/changelog b/debian/changelog
index f6b9ea0..554c7b2 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,9 +1,14 @@
-golang-github-vishvananda-netns (0.0~git20211101.5004558-2) UNRELEASED; urgency=medium
+golang-github-vishvananda-netns (0.0~git20230405.16c2fa0-1) UNRELEASED; urgency=medium
 
+  [ Tianon Gravi ]
   * Team upload.
   * Remove self from Uploaders
 
- -- Tianon Gravi <tianon@debian.org>  Wed, 23 Feb 2022 11:11:33 -0800
+  [ Debian Janitor ]
+  * New upstream snapshot.
+  * New upstream snapshot.
+
+ -- Tianon Gravi <tianon@debian.org>  Mon, 22 May 2023 10:21:37 -0000
 
 golang-github-vishvananda-netns (0.0~git20211101.5004558-1) unstable; urgency=medium
 
diff --git a/doc.go b/doc.go
new file mode 100644
index 0000000..cd4093a
--- /dev/null
+++ b/doc.go
@@ -0,0 +1,9 @@
+// Package netns allows ultra-simple network namespace handling. NsHandles
+// can be retrieved and set. Note that the current namespace is thread
+// local so actions that set and reset namespaces should use LockOSThread
+// to make sure the namespace doesn't change due to a goroutine switch.
+// It is best to close NsHandles when you are done with them. This can be
+// accomplished via a `defer ns.Close()` on the handle. Changing namespaces
+// requires elevated privileges, so in most cases this code needs to be run
+// as root.
+package netns
diff --git a/go.mod b/go.mod
index 9cdf577..4a240d9 100644
--- a/go.mod
+++ b/go.mod
@@ -1,5 +1,5 @@
 module github.com/vishvananda/netns
 
-go 1.12
+go 1.17
 
-require golang.org/x/sys v0.0.0-20200217220822-9197077df867
+require golang.org/x/sys v0.2.0
diff --git a/go.sum b/go.sum
index 1d4adf4..beac707 100644
--- a/go.sum
+++ b/go.sum
@@ -1,2 +1,2 @@
-golang.org/x/sys v0.0.0-20200217220822-9197077df867 h1:JoRuNIf+rpHl+VhScRQQvzbHed86tKkqwPMV34T8myw=
-golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/netns_linux.go b/netns_linux.go
index 6be5c55..8c9b177 100644
--- a/netns_linux.go
+++ b/netns_linux.go
@@ -1,57 +1,55 @@
-//go:build linux && go1.10
-// +build linux,go1.10
-
 package netns
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"path"
 	"path/filepath"
 	"strconv"
 	"strings"
-	"syscall"
 
 	"golang.org/x/sys/unix"
 )
 
-// Deprecated: use syscall pkg instead (go >= 1.5 needed).
+// Deprecated: use golang.org/x/sys/unix pkg instead.
 const (
-	CLONE_NEWUTS  = 0x04000000   /* New utsname group? */
-	CLONE_NEWIPC  = 0x08000000   /* New ipcs */
-	CLONE_NEWUSER = 0x10000000   /* New user namespace */
-	CLONE_NEWPID  = 0x20000000   /* New pid namespace */
-	CLONE_NEWNET  = 0x40000000   /* New network namespace */
-	CLONE_IO      = 0x80000000   /* Get io context */
-	bindMountPath = "/run/netns" /* Bind mount path for named netns */
+	CLONE_NEWUTS  = unix.CLONE_NEWUTS  /* New utsname group? */
+	CLONE_NEWIPC  = unix.CLONE_NEWIPC  /* New ipcs */
+	CLONE_NEWUSER = unix.CLONE_NEWUSER /* New user namespace */
+	CLONE_NEWPID  = unix.CLONE_NEWPID  /* New pid namespace */
+	CLONE_NEWNET  = unix.CLONE_NEWNET  /* New network namespace */
+	CLONE_IO      = unix.CLONE_IO      /* Get io context */
 )
 
-// Setns sets namespace using syscall. Note that this should be a method
-// in syscall but it has not been added.
-func Setns(ns NsHandle, nstype int) (err error) {
+const bindMountPath = "/run/netns" /* Bind mount path for named netns */
+
+// Setns sets namespace using golang.org/x/sys/unix.Setns.
+//
+// Deprecated: Use golang.org/x/sys/unix.Setns instead.
+func Setns(ns NsHandle, nstype int) error {
 	return unix.Setns(int(ns), nstype)
 }
 
 // Set sets the current network namespace to the namespace represented
 // by NsHandle.
-func Set(ns NsHandle) (err error) {
-	return Setns(ns, CLONE_NEWNET)
+func Set(ns NsHandle) error {
+	return unix.Setns(int(ns), unix.CLONE_NEWNET)
 }
 
 // New creates a new network namespace, sets it as current and returns
 // a handle to it.
-func New() (ns NsHandle, err error) {
-	if err := unix.Unshare(CLONE_NEWNET); err != nil {
+func New() (NsHandle, error) {
+	if err := unix.Unshare(unix.CLONE_NEWNET); err != nil {
 		return -1, err
 	}
 	return Get()
 }
 
-// NewNamed creates a new named network namespace and returns a handle to it
+// NewNamed creates a new named network namespace, sets it as current,
+// and returns a handle to it
 func NewNamed(name string) (NsHandle, error) {
 	if _, err := os.Stat(bindMountPath); os.IsNotExist(err) {
-		err = os.MkdirAll(bindMountPath, 0755)
+		err = os.MkdirAll(bindMountPath, 0o755)
 		if err != nil {
 			return None(), err
 		}
@@ -64,15 +62,17 @@ func NewNamed(name string) (NsHandle, error) {
 
 	namedPath := path.Join(bindMountPath, name)
 
-	f, err := os.OpenFile(namedPath, os.O_CREATE|os.O_EXCL, 0444)
+	f, err := os.OpenFile(namedPath, os.O_CREATE|os.O_EXCL, 0o444)
 	if err != nil {
+		newNs.Close()
 		return None(), err
 	}
 	f.Close()
 
-	nsPath := fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), syscall.Gettid())
-	err = syscall.Mount(nsPath, namedPath, "bind", syscall.MS_BIND, "")
+	nsPath := fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid())
+	err = unix.Mount(nsPath, namedPath, "bind", unix.MS_BIND, "")
 	if err != nil {
+		newNs.Close()
 		return None(), err
 	}
 
@@ -83,7 +83,7 @@ func NewNamed(name string) (NsHandle, error) {
 func DeleteNamed(name string) error {
 	namedPath := path.Join(bindMountPath, name)
 
-	err := syscall.Unmount(namedPath, syscall.MNT_DETACH)
+	err := unix.Unmount(namedPath, unix.MNT_DETACH)
 	if err != nil {
 		return err
 	}
@@ -109,7 +109,7 @@ func GetFromPath(path string) (NsHandle, error) {
 // GetFromName gets a handle to a named network namespace such as one
 // created by `ip netns add`.
 func GetFromName(name string) (NsHandle, error) {
-	return GetFromPath(fmt.Sprintf("/var/run/netns/%s", name))
+	return GetFromPath(filepath.Join(bindMountPath, name))
 }
 
 // GetFromPid gets a handle to the network namespace of a given pid.
@@ -135,7 +135,7 @@ func GetFromDocker(id string) (NsHandle, error) {
 
 // borrowed from docker/utils/utils.go
 func findCgroupMountpoint(cgroupType string) (int, string, error) {
-	output, err := ioutil.ReadFile("/proc/mounts")
+	output, err := os.ReadFile("/proc/mounts")
 	if err != nil {
 		return -1, "", err
 	}
@@ -165,7 +165,7 @@ func findCgroupMountpoint(cgroupType string) (int, string, error) {
 // borrowed from docker/utils/utils.go
 // modified to get the docker pid instead of using /proc/self
 func getDockerCgroup(cgroupVer int, cgroupType string) (string, error) {
-	dockerpid, err := ioutil.ReadFile("/var/run/docker.pid")
+	dockerpid, err := os.ReadFile("/var/run/docker.pid")
 	if err != nil {
 		return "", err
 	}
@@ -177,7 +177,7 @@ func getDockerCgroup(cgroupVer int, cgroupType string) (string, error) {
 	if err != nil {
 		return "", err
 	}
-	output, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cgroup", pid))
+	output, err := os.ReadFile(fmt.Sprintf("/proc/%d/cgroup", pid))
 	if err != nil {
 		return "", err
 	}
@@ -217,11 +217,12 @@ func getPidForContainer(id string) (int, error) {
 	id += "*"
 
 	var pidFile string
-	if cgroupVer == 1 {
+	switch cgroupVer {
+	case 1:
 		pidFile = "tasks"
-	} else if cgroupVer == 2 {
+	case 2:
 		pidFile = "cgroup.procs"
-	} else {
+	default:
 		return -1, fmt.Errorf("Invalid cgroup version '%d'", cgroupVer)
 	}
 
@@ -264,7 +265,7 @@ func getPidForContainer(id string) (int, error) {
 		return pid, fmt.Errorf("Unable to find container: %v", id[:len(id)-1])
 	}
 
-	output, err := ioutil.ReadFile(filename)
+	output, err := os.ReadFile(filename)
 	if err != nil {
 		return pid, err
 	}
@@ -276,7 +277,7 @@ func getPidForContainer(id string) (int, error) {
 
 	pid, err = strconv.Atoi(result[0])
 	if err != nil {
-		return pid, fmt.Errorf("Invalid pid '%s': %s", result[0], err)
+		return pid, fmt.Errorf("Invalid pid '%s': %w", result[0], err)
 	}
 
 	return pid, nil
diff --git a/netns_test.go b/netns_linux_test.go
similarity index 93%
rename from netns_test.go
rename to netns_linux_test.go
index e51981c..b8d9bd9 100644
--- a/netns_test.go
+++ b/netns_linux_test.go
@@ -24,7 +24,9 @@ func TestGetNewSetDelete(t *testing.T) {
 	if err := Set(origns); err != nil {
 		t.Fatal(err)
 	}
-	newns.Close()
+	if err := newns.Close(); err != nil {
+		t.Error("Failed to close ns", err)
+	}
 	if newns.IsOpen() {
 		t.Fatal("newns still open after close", newns)
 	}
diff --git a/netns_others.go b/netns_others.go
new file mode 100644
index 0000000..f444f6e
--- /dev/null
+++ b/netns_others.go
@@ -0,0 +1,56 @@
+//go:build !linux
+// +build !linux
+
+package netns
+
+import "errors"
+
+var ErrNotImplemented = errors.New("not implemented")
+
+// Setns sets namespace using golang.org/x/sys/unix.Setns on Linux. It
+// is not implemented on other platforms.
+//
+// Deprecated: Use golang.org/x/sys/unix.Setns instead.
+func Setns(ns NsHandle, nstype int) error {
+	return ErrNotImplemented
+}
+
+func Set(ns NsHandle) error {
+	return ErrNotImplemented
+}
+
+func New() (NsHandle, error) {
+	return -1, ErrNotImplemented
+}
+
+func NewNamed(name string) (NsHandle, error) {
+	return -1, ErrNotImplemented
+}
+
+func DeleteNamed(name string) error {
+	return ErrNotImplemented
+}
+
+func Get() (NsHandle, error) {
+	return -1, ErrNotImplemented
+}
+
+func GetFromPath(path string) (NsHandle, error) {
+	return -1, ErrNotImplemented
+}
+
+func GetFromName(name string) (NsHandle, error) {
+	return -1, ErrNotImplemented
+}
+
+func GetFromPid(pid int) (NsHandle, error) {
+	return -1, ErrNotImplemented
+}
+
+func GetFromThread(pid int, tid int) (NsHandle, error) {
+	return -1, ErrNotImplemented
+}
+
+func GetFromDocker(id string) (NsHandle, error) {
+	return -1, ErrNotImplemented
+}
diff --git a/netns_unspecified.go b/netns_unspecified.go
deleted file mode 100644
index d06af62..0000000
--- a/netns_unspecified.go
+++ /dev/null
@@ -1,43 +0,0 @@
-// +build !linux
-
-package netns
-
-import (
-	"errors"
-)
-
-var (
-	ErrNotImplemented = errors.New("not implemented")
-)
-
-func Set(ns NsHandle) (err error) {
-	return ErrNotImplemented
-}
-
-func New() (ns NsHandle, err error) {
-	return -1, ErrNotImplemented
-}
-
-func Get() (NsHandle, error) {
-	return -1, ErrNotImplemented
-}
-
-func GetFromPath(path string) (NsHandle, error) {
-	return -1, ErrNotImplemented
-}
-
-func GetFromName(name string) (NsHandle, error) {
-	return -1, ErrNotImplemented
-}
-
-func GetFromPid(pid int) (NsHandle, error) {
-	return -1, ErrNotImplemented
-}
-
-func GetFromThread(pid, tid int) (NsHandle, error) {
-	return -1, ErrNotImplemented
-}
-
-func GetFromDocker(id string) (NsHandle, error) {
-	return -1, ErrNotImplemented
-}
diff --git a/netns.go b/nshandle_linux.go
similarity index 75%
rename from netns.go
rename to nshandle_linux.go
index 116befd..1baffb6 100644
--- a/netns.go
+++ b/nshandle_linux.go
@@ -1,11 +1,3 @@
-// Package netns allows ultra-simple network namespace handling. NsHandles
-// can be retrieved and set. Note that the current namespace is thread
-// local so actions that set and reset namespaces should use LockOSThread
-// to make sure the namespace doesn't change due to a goroutine switch.
-// It is best to close NsHandles when you are done with them. This can be
-// accomplished via a `defer ns.Close()` on the handle. Changing namespaces
-// requires elevated privileges, so in most cases this code needs to be run
-// as root.
 package netns
 
 import (
@@ -38,7 +30,7 @@ func (ns NsHandle) Equal(other NsHandle) bool {
 // String shows the file descriptor number and its dev and inode.
 func (ns NsHandle) String() string {
 	if ns == -1 {
-		return "NS(None)"
+		return "NS(none)"
 	}
 	var s unix.Stat_t
 	if err := unix.Fstat(int(ns), &s); err != nil {
@@ -71,7 +63,7 @@ func (ns *NsHandle) Close() error {
 	if err := unix.Close(int(*ns)); err != nil {
 		return err
 	}
-	(*ns) = -1
+	*ns = -1
 	return nil
 }
 
diff --git a/nshandle_others.go b/nshandle_others.go
new file mode 100644
index 0000000..af727bc
--- /dev/null
+++ b/nshandle_others.go
@@ -0,0 +1,45 @@
+//go:build !linux
+// +build !linux
+
+package netns
+
+// NsHandle is a handle to a network namespace. It can only be used on Linux,
+// but provides stub methods on other platforms.
+type NsHandle int
+
+// Equal determines if two network handles refer to the same network
+// namespace. It is only implemented on Linux.
+func (ns NsHandle) Equal(_ NsHandle) bool {
+	return false
+}
+
+// String shows the file descriptor number and its dev and inode.
+// It is only implemented on Linux, and returns "NS(none)" on other
+// platforms.
+func (ns NsHandle) String() string {
+	return "NS(none)"
+}
+
+// UniqueId returns a string which uniquely identifies the namespace
+// associated with the network handle. It is only implemented on Linux,
+// and returns "NS(none)" on other platforms.
+func (ns NsHandle) UniqueId() string {
+	return "NS(none)"
+}
+
+// IsOpen returns true if Close() has not been called. It is only implemented
+// on Linux and always returns false on other platforms.
+func (ns NsHandle) IsOpen() bool {
+	return false
+}
+
+// Close closes the NsHandle and resets its file descriptor to -1.
+// It is only implemented on Linux.
+func (ns *NsHandle) Close() error {
+	return nil
+}
+
+// None gets an empty (closed) NsHandle.
+func None() NsHandle {
+	return NsHandle(-1)
+}

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/share/gocode/src/github.com/vishvananda/netns/doc.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/vishvananda/netns/netns_linux_test.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/vishvananda/netns/netns_others.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/vishvananda/netns/nshandle_linux.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/vishvananda/netns/nshandle_others.go

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/share/gocode/src/github.com/vishvananda/netns/netns.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/vishvananda/netns/netns_test.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/vishvananda/netns/netns_unspecified.go

No differences were encountered in the control files

More details

Full run details