File: linux_ns_nopath.go

package info (click to toggle)
golang-github-opencontainers-runtime-tools 0.9.0%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 1,108 kB
  • sloc: sh: 557; makefile: 104
file content (132 lines) | stat: -rw-r--r-- 4,081 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package main

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"

	"github.com/mndrix/tap-go"
	rspec "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/opencontainers/runtime-tools/specerror"
	"github.com/opencontainers/runtime-tools/validation/util"
)

func printDiag(t *tap.T, diagActual, diagExpected, diagNsType string, errNs error) {
	specErr := specerror.NewError(specerror.NSNewNSWithoutPath,
		errNs, rspec.Version)
	diagnostic := map[string]string{
		"actual":         diagActual,
		"expected":       diagExpected,
		"namespace type": diagNsType,
		"level":          specErr.(*specerror.Error).Err.Level.String(),
		"reference":      specErr.(*specerror.Error).Err.Reference,
	}
	t.YAML(diagnostic)
}

func testNamespaceNoPath(t *tap.T) error {
	var errNs error
	diagActual := ""
	diagExpected := ""
	diagNsType := ""

	// To be able to print out diagnostics for all kinds of error cases
	// at the end of the tests, we make use of defer function. To do that,
	// each error handling routine should set diagActual, diagExpected,
	// diagNsType, and errNs, before returning an error.
	defer func() {
		if errNs != nil {
			printDiag(t, diagActual, diagExpected, diagNsType, errNs)
		}
	}()

	hostNsPath := fmt.Sprintf("/proc/%d/ns", os.Getpid())
	hostNsInodes := map[string]string{}

	for _, nsName := range util.ProcNamespaces {
		nsPathAbs := filepath.Join(hostNsPath, nsName)
		nsInode, err := os.Readlink(nsPathAbs)
		if err != nil {
			errNs = fmt.Errorf("cannot resolve symlink %q: %v", nsPathAbs, err)
			diagActual = fmt.Sprintf("err == %v", errNs)
			diagExpected = "err == nil"
			diagNsType = nsName
			return errNs
		}
		hostNsInodes[nsName] = nsInode
	}

	g, err := util.GetDefaultGenerator()
	if err != nil {
		errNs = fmt.Errorf("cannot get the default generator: %v", err)
		diagActual = fmt.Sprintf("err == %v", errNs)
		diagExpected = "err == nil"
		// NOTE: we don't have a namespace type
		return errNs
	}

	// As the namespaces, cgroups and user, are not set by GetDefaultGenerator(),
	// others are set by default. We just set them explicitly to avoid confusion.
	g.AddOrReplaceLinuxNamespace("cgroup", "")
	g.AddOrReplaceLinuxNamespace("ipc", "")
	g.AddOrReplaceLinuxNamespace("mount", "")
	g.AddOrReplaceLinuxNamespace("network", "")
	g.AddOrReplaceLinuxNamespace("pid", "")
	g.AddOrReplaceLinuxNamespace("user", "")
	g.AddOrReplaceLinuxNamespace("uts", "")

	// For user namespaces, we need to set uid/gid maps to create a container
	g.AddLinuxUIDMapping(uint32(1000), uint32(0), uint32(1000))
	g.AddLinuxGIDMapping(uint32(1000), uint32(0), uint32(1000))

	err = util.RuntimeOutsideValidate(g, t, func(config *rspec.Spec, t *tap.T, state *rspec.State) error {
		containerNsPath := fmt.Sprintf("/proc/%d/ns", state.Pid)

		for _, nsName := range util.ProcNamespaces {
			nsPathAbs := filepath.Join(containerNsPath, nsName)
			nsInode, err := os.Readlink(nsPathAbs)
			if err != nil {
				errNs = fmt.Errorf("cannot resolve symlink %q: %v", nsPathAbs, err)
				diagActual = fmt.Sprintf("err == %v", errNs)
				diagExpected = "err == nil"
				diagNsType = nsName
				return errNs
			}

			t.Ok(hostNsInodes[nsName] != nsInode, fmt.Sprintf("create namespace %s without path", nsName))
			if hostNsInodes[nsName] == nsInode {
				// NOTE: for such inode match cases, we should print out diagnostics
				// for each case, not only at the end of tests. So we should simply
				// call once printDiag(), then continue testing next namespaces.
				// Thus we don't need to set diagActual, diagExpected, diagNsType, etc.
				printDiag(t, nsInode, fmt.Sprintf("!= %s", hostNsInodes[nsName]), nsName,
					fmt.Errorf("both namespaces for %s have the same inode %s", nsName, nsInode))
				continue
			}
		}

		return nil
	})
	if err != nil {
		errNs = fmt.Errorf("cannot run validation tests: %v", err)
	}

	return errNs
}

func main() {
	t := tap.New()
	t.Header(0)

	if "linux" != runtime.GOOS {
		t.Skip(1, fmt.Sprintf("linux-specific namespace test"))
	}

	err := testNamespaceNoPath(t)
	if err != nil {
		t.Fail(err.Error())
	}

	t.AutoPlan()
}