github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/pkg/nsenter/nsenter_test.go (about) 1 // Copyright (c) 2018 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 package nsenter 7 8 import ( 9 "fmt" 10 "io/ioutil" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "runtime" 15 "strconv" 16 "syscall" 17 "testing" 18 19 "github.com/stretchr/testify/assert" 20 "golang.org/x/sys/unix" 21 22 ktu "github.com/kata-containers/runtime/pkg/katatestutils" 23 ) 24 25 const testPID = 12345 26 27 var tu = ktu.NewTestConstraint(true) 28 29 func TestGetNSPathFromPID(t *testing.T) { 30 for nsType := range CloneFlagsTable { 31 expectedPath := fmt.Sprintf("/proc/%d/ns/%s", testPID, nsType) 32 path := getNSPathFromPID(testPID, nsType) 33 assert.Equal(t, path, expectedPath) 34 } 35 } 36 37 func TestGetCurrentThreadNSPath(t *testing.T) { 38 runtime.LockOSThread() 39 defer runtime.UnlockOSThread() 40 41 currentPID := os.Getpid() 42 currentTID := unix.Gettid() 43 for nsType := range CloneFlagsTable { 44 expectedPath := fmt.Sprintf("/proc/%d/task/%d/ns/%s", currentPID, currentTID, nsType) 45 path := getCurrentThreadNSPath(nsType) 46 assert.Equal(t, path, expectedPath) 47 } 48 } 49 50 func TestGetFileFromNSEmptyNSPathFailure(t *testing.T) { 51 nsFile, err := getFileFromNS("") 52 assert.NotNil(t, err, "Empty path should result as a failure") 53 assert.Nil(t, nsFile, "The file handler returned should be nil") 54 } 55 56 func TestGetFileFromNSNotExistingNSPathFailure(t *testing.T) { 57 nsFile, err := ioutil.TempFile("", "not-existing-ns-path") 58 assert.NoError(t, err) 59 nsFilePath := nsFile.Name() 60 nsFile.Close() 61 62 assert.NoError(t, os.Remove(nsFilePath)) 63 64 nsFile, err = getFileFromNS(nsFilePath) 65 assert.NotNil(t, err, "Not existing path should result as a failure") 66 assert.Nil(t, nsFile, "The file handler returned should be nil") 67 } 68 69 func TestGetFileFromNSWrongNSPathFailure(t *testing.T) { 70 nsFile, err := ioutil.TempFile("", "wrong-ns-path") 71 assert.NoError(t, err) 72 nsFilePath := nsFile.Name() 73 nsFile.Close() 74 75 defer os.Remove(nsFilePath) 76 77 nsFile, err = getFileFromNS(nsFilePath) 78 assert.NotNil(t, err, "Should fail because wrong filesystem") 79 assert.Nil(t, nsFile, "The file handler returned should be nil") 80 } 81 82 func TestGetFileFromNSSuccessful(t *testing.T) { 83 for nsType := range CloneFlagsTable { 84 nsFilePath := fmt.Sprintf("/proc/self/ns/%s", string(nsType)) 85 nsFile, err := getFileFromNS(nsFilePath) 86 assert.Nil(t, err, "Should have succeeded: %v", err) 87 assert.NotNil(t, nsFile, "The file handler should not be nil") 88 if nsFile != nil { 89 nsFile.Close() 90 } 91 } 92 } 93 94 func startSleepBinary(duration int, cloneFlags int) (int, error) { 95 sleepBinName := "sleep" 96 sleepPath, err := exec.LookPath(sleepBinName) 97 if err != nil { 98 return -1, fmt.Errorf("Could not find %q: %v", sleepBinName, err) 99 } 100 101 cmd := exec.Command(sleepPath, strconv.Itoa(duration)) 102 cmd.SysProcAttr = &syscall.SysProcAttr{ 103 Cloneflags: uintptr(cloneFlags), 104 } 105 106 if err := cmd.Start(); err != nil { 107 return -1, err 108 } 109 110 return cmd.Process.Pid, nil 111 } 112 113 func TestSetNSNilFileHandlerFailure(t *testing.T) { 114 err := setNS(nil, "") 115 assert.NotNil(t, err, "Should fail because file handler is nil") 116 } 117 118 func TestSetNSUnknownNSTypeFailure(t *testing.T) { 119 file := &os.File{} 120 err := setNS(file, "") 121 assert.NotNil(t, err, "Should fail because unknown ns type") 122 } 123 124 func TestSetNSWrongFileFailure(t *testing.T) { 125 nsFile, err := ioutil.TempFile("", "wrong-ns-path") 126 assert.NoError(t, err) 127 defer func() { 128 nsFilePath := nsFile.Name() 129 nsFile.Close() 130 os.Remove(nsFilePath) 131 }() 132 133 err = setNS(nsFile, NSTypeIPC) 134 assert.NotNil(t, err, "Should fail because file is not a namespace") 135 } 136 137 func supportedNamespaces() []Namespace { 138 var list []Namespace 139 var ns = []Namespace{ 140 {Type: NSTypeCGroup}, 141 {Type: NSTypeIPC}, 142 {Type: NSTypeNet}, 143 {Type: NSTypePID}, 144 {Type: NSTypeUTS}, 145 } 146 147 for _, n := range ns { 148 if _, err := os.Stat(fmt.Sprint("/proc/self/ns/", string(n.Type))); err == nil { 149 list = append(list, n) 150 } 151 } 152 153 return list 154 } 155 156 func testToRunNil() error { 157 return nil 158 } 159 160 func TestNsEnterEmptyPathAndPIDFromNSListFailure(t *testing.T) { 161 err := NsEnter(supportedNamespaces(), testToRunNil) 162 assert.NotNil(t, err, "Should fail because neither a path nor a PID"+ 163 " has been provided by every namespace of the list") 164 } 165 166 func TestNsEnterEmptyNamespaceListSuccess(t *testing.T) { 167 err := NsEnter([]Namespace{}, testToRunNil) 168 assert.Nil(t, err, "Should not fail since closure should return nil: %v", err) 169 } 170 171 func TestNsEnterSuccessful(t *testing.T) { 172 if tu.NotValid(ktu.NeedRoot()) { 173 t.Skip(ktu.TestDisabledNeedRoot) 174 } 175 nsList := supportedNamespaces() 176 sleepDuration := 60 177 178 cloneFlags := 0 179 for _, ns := range nsList { 180 cloneFlags |= CloneFlagsTable[ns.Type] 181 } 182 183 sleepPID, err := startSleepBinary(sleepDuration, cloneFlags) 184 assert.NoError(t, err) 185 defer func() { 186 if sleepPID > 1 { 187 unix.Kill(sleepPID, syscall.SIGKILL) 188 } 189 }() 190 191 for idx := range nsList { 192 nsList[idx].Path = getNSPathFromPID(sleepPID, nsList[idx].Type) 193 nsList[idx].PID = sleepPID 194 } 195 196 var sleepPIDFromNsEnter int 197 198 testToRun := func() error { 199 sleepPIDFromNsEnter, err = startSleepBinary(sleepDuration, 0) 200 if err != nil { 201 return err 202 } 203 204 return nil 205 } 206 207 err = NsEnter(nsList, testToRun) 208 assert.Nil(t, err, "%v", err) 209 210 defer func() { 211 if sleepPIDFromNsEnter > 1 { 212 unix.Kill(sleepPIDFromNsEnter, syscall.SIGKILL) 213 } 214 }() 215 216 for _, ns := range nsList { 217 nsPathEntered := getNSPathFromPID(sleepPIDFromNsEnter, ns.Type) 218 219 // Here we are trying to resolve the path but it fails because 220 // namespaces links don't really exist. For this reason, the 221 // call to EvalSymlinks will fail when it will try to stat the 222 // resolved path found. As we only care about the path, we can 223 // retrieve it from the PathError structure. 224 evalExpectedNSPath, err := filepath.EvalSymlinks(ns.Path) 225 if err != nil { 226 evalExpectedNSPath = err.(*os.PathError).Path 227 } 228 229 // Same thing here, resolving the namespace path. 230 evalNSEnteredPath, err := filepath.EvalSymlinks(nsPathEntered) 231 if err != nil { 232 evalNSEnteredPath = err.(*os.PathError).Path 233 } 234 235 _, evalExpectedNS := filepath.Split(evalExpectedNSPath) 236 _, evalNSEntered := filepath.Split(evalNSEnteredPath) 237 238 assert.Equal(t, evalExpectedNS, evalNSEntered) 239 } 240 }