mirror of
https://github.com/aljazceru/kata-containers.git
synced 2026-01-04 15:04:25 +01:00
Depending of the linux versions some namespaces are supported or not. To avoid failures running containers and unit tests in old linux kernels, only the supported namespaces should be used. Depends-on: github.com/kata-containers/tests#1414 fixes #228 Signed-off-by: Julio Montes <julio.montes@intel.com>
244 lines
5.9 KiB
Go
244 lines
5.9 KiB
Go
// Copyright (c) 2018 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
package nsenter
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strconv"
|
|
"syscall"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
const testPID = 12345
|
|
|
|
func TestGetNSPathFromPID(t *testing.T) {
|
|
for nsType := range CloneFlagsTable {
|
|
expectedPath := fmt.Sprintf("/proc/%d/ns/%s", testPID, nsType)
|
|
path := getNSPathFromPID(testPID, nsType)
|
|
assert.Equal(t, path, expectedPath)
|
|
}
|
|
}
|
|
|
|
func TestGetCurrentThreadNSPath(t *testing.T) {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
|
|
currentPID := os.Getpid()
|
|
currentTID := unix.Gettid()
|
|
for nsType := range CloneFlagsTable {
|
|
expectedPath := fmt.Sprintf("/proc/%d/task/%d/ns/%s", currentPID, currentTID, nsType)
|
|
path := getCurrentThreadNSPath(nsType)
|
|
assert.Equal(t, path, expectedPath)
|
|
}
|
|
}
|
|
|
|
func TestGetFileFromNSEmptyNSPathFailure(t *testing.T) {
|
|
nsFile, err := getFileFromNS("")
|
|
assert.NotNil(t, err, "Empty path should result as a failure")
|
|
assert.Nil(t, nsFile, "The file handler returned should be nil")
|
|
}
|
|
|
|
func TestGetFileFromNSNotExistingNSPathFailure(t *testing.T) {
|
|
nsFile, err := ioutil.TempFile("", "not-existing-ns-path")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
nsFilePath := nsFile.Name()
|
|
nsFile.Close()
|
|
|
|
if err := os.Remove(nsFilePath); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
nsFile, err = getFileFromNS(nsFilePath)
|
|
assert.NotNil(t, err, "Not existing path should result as a failure")
|
|
assert.Nil(t, nsFile, "The file handler returned should be nil")
|
|
}
|
|
|
|
func TestGetFileFromNSWrongNSPathFailure(t *testing.T) {
|
|
nsFile, err := ioutil.TempFile("", "wrong-ns-path")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
nsFilePath := nsFile.Name()
|
|
nsFile.Close()
|
|
|
|
defer os.Remove(nsFilePath)
|
|
|
|
nsFile, err = getFileFromNS(nsFilePath)
|
|
assert.NotNil(t, err, "Should fail because wrong filesystem")
|
|
assert.Nil(t, nsFile, "The file handler returned should be nil")
|
|
}
|
|
|
|
func TestGetFileFromNSSuccessful(t *testing.T) {
|
|
for nsType := range CloneFlagsTable {
|
|
nsFilePath := fmt.Sprintf("/proc/self/ns/%s", string(nsType))
|
|
nsFile, err := getFileFromNS(nsFilePath)
|
|
assert.Nil(t, err, "Should have succeeded: %v", err)
|
|
assert.NotNil(t, nsFile, "The file handler should not be nil")
|
|
if nsFile != nil {
|
|
nsFile.Close()
|
|
}
|
|
}
|
|
}
|
|
|
|
func startSleepBinary(duration int, cloneFlags int) (int, error) {
|
|
sleepBinName := "sleep"
|
|
sleepPath, err := exec.LookPath(sleepBinName)
|
|
if err != nil {
|
|
return -1, fmt.Errorf("Could not find %q: %v", sleepBinName, err)
|
|
}
|
|
|
|
cmd := exec.Command(sleepPath, strconv.Itoa(duration))
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
Cloneflags: uintptr(cloneFlags),
|
|
}
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
return -1, err
|
|
}
|
|
|
|
return cmd.Process.Pid, nil
|
|
}
|
|
|
|
func TestSetNSNilFileHandlerFailure(t *testing.T) {
|
|
err := setNS(nil, "")
|
|
assert.NotNil(t, err, "Should fail because file handler is nil")
|
|
}
|
|
|
|
func TestSetNSUnknownNSTypeFailure(t *testing.T) {
|
|
file := &os.File{}
|
|
err := setNS(file, "")
|
|
assert.NotNil(t, err, "Should fail because unknown ns type")
|
|
}
|
|
|
|
func TestSetNSWrongFileFailure(t *testing.T) {
|
|
nsFile, err := ioutil.TempFile("", "wrong-ns-path")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() {
|
|
nsFilePath := nsFile.Name()
|
|
nsFile.Close()
|
|
os.Remove(nsFilePath)
|
|
}()
|
|
|
|
err = setNS(nsFile, NSTypeIPC)
|
|
assert.NotNil(t, err, "Should fail because file is not a namespace")
|
|
}
|
|
|
|
func supportedNamespaces() []Namespace {
|
|
var list []Namespace
|
|
var ns = []Namespace{
|
|
{Type: NSTypeCGroup},
|
|
{Type: NSTypeIPC},
|
|
{Type: NSTypeNet},
|
|
{Type: NSTypePID},
|
|
{Type: NSTypeUTS},
|
|
}
|
|
|
|
for _, n := range ns {
|
|
if _, err := os.Stat(fmt.Sprint("/proc/self/ns/", string(n.Type))); err == nil {
|
|
list = append(list, n)
|
|
}
|
|
}
|
|
|
|
return list
|
|
}
|
|
|
|
func testToRunNil() error {
|
|
return nil
|
|
}
|
|
|
|
func TestNsEnterEmptyPathAndPIDFromNSListFailure(t *testing.T) {
|
|
err := NsEnter(supportedNamespaces(), testToRunNil)
|
|
assert.NotNil(t, err, "Should fail because neither a path nor a PID"+
|
|
" has been provided by every namespace of the list")
|
|
}
|
|
|
|
func TestNsEnterEmptyNamespaceListSuccess(t *testing.T) {
|
|
err := NsEnter([]Namespace{}, testToRunNil)
|
|
assert.Nil(t, err, "Should not fail since closure should return nil: %v", err)
|
|
}
|
|
|
|
func TestNsEnterSuccessful(t *testing.T) {
|
|
nsList := supportedNamespaces()
|
|
sleepDuration := 60
|
|
|
|
cloneFlags := 0
|
|
for _, ns := range nsList {
|
|
cloneFlags |= CloneFlagsTable[ns.Type]
|
|
}
|
|
|
|
sleepPID, err := startSleepBinary(sleepDuration, cloneFlags)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() {
|
|
if sleepPID > 1 {
|
|
unix.Kill(sleepPID, syscall.SIGKILL)
|
|
}
|
|
}()
|
|
|
|
for idx := range nsList {
|
|
nsList[idx].Path = getNSPathFromPID(sleepPID, nsList[idx].Type)
|
|
nsList[idx].PID = sleepPID
|
|
}
|
|
|
|
var sleepPIDFromNsEnter int
|
|
|
|
testToRun := func() error {
|
|
sleepPIDFromNsEnter, err = startSleepBinary(sleepDuration, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
err = NsEnter(nsList, testToRun)
|
|
assert.Nil(t, err, "%v", err)
|
|
|
|
defer func() {
|
|
if sleepPIDFromNsEnter > 1 {
|
|
unix.Kill(sleepPIDFromNsEnter, syscall.SIGKILL)
|
|
}
|
|
}()
|
|
|
|
for _, ns := range nsList {
|
|
nsPathEntered := getNSPathFromPID(sleepPIDFromNsEnter, ns.Type)
|
|
|
|
// Here we are trying to resolve the path but it fails because
|
|
// namespaces links don't really exist. For this reason, the
|
|
// call to EvalSymlinks will fail when it will try to stat the
|
|
// resolved path found. As we only care about the path, we can
|
|
// retrieve it from the PathError structure.
|
|
evalExpectedNSPath, err := filepath.EvalSymlinks(ns.Path)
|
|
if err != nil {
|
|
evalExpectedNSPath = err.(*os.PathError).Path
|
|
}
|
|
|
|
// Same thing here, resolving the namespace path.
|
|
evalNSEnteredPath, err := filepath.EvalSymlinks(nsPathEntered)
|
|
if err != nil {
|
|
evalNSEnteredPath = err.(*os.PathError).Path
|
|
}
|
|
|
|
_, evalExpectedNS := filepath.Split(evalExpectedNSPath)
|
|
_, evalNSEntered := filepath.Split(evalNSEnteredPath)
|
|
|
|
assert.Equal(t, evalExpectedNS, evalNSEntered)
|
|
}
|
|
}
|