runtime: remove nsenter

remove code for nsenter

Fixes: #1081

Signed-off-by: bin liu <bin@hyper.sh>
This commit is contained in:
bin liu 2020-11-05 19:40:48 +08:00
parent e3510be867
commit b8414045bf
4 changed files with 0 additions and 460 deletions

View File

@ -1,29 +0,0 @@
// Copyright (c) 2016 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package virtcontainers
// nsenter is a spawner implementation for the nsenter util-linux command.
type nsenter struct {
ContConfig ContainerConfig
}
const (
// NsenterCmd is the command used to start nsenter.
nsenterCmd = "nsenter"
)
// formatArgs is the spawner command formatting implementation for nsenter.
func (n *nsenter) formatArgs(args []string) ([]string, error) {
var newArgs []string
pid := "-1"
// TODO: Retrieve container PID from container ID
newArgs = append(newArgs, nsenterCmd+" --target "+pid+" --mount --uts --ipc --net --pid")
newArgs = append(newArgs, args...)
return newArgs, nil
}

View File

@ -1,29 +0,0 @@
// Copyright (c) 2016 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package virtcontainers
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func testNsEnterFormatArgs(t *testing.T, args []string, expected string) {
nsenter := &nsenter{}
cmd, err := nsenter.formatArgs(args)
assert.NoError(t, err)
assert.Equal(t, strings.Join(cmd, " "), expected)
}
func TestNsEnterFormatArgsHello(t *testing.T) {
expectedCmd := "nsenter --target -1 --mount --uts --ipc --net --pid echo hello"
args := []string{"echo", "hello"}
testNsEnterFormatArgs(t, args, expectedCmd)
}

View File

@ -6,29 +6,6 @@
package nsenter
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strconv"
"sync"
"syscall"
"golang.org/x/sys/unix"
)
// Filesystems constants.
const (
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/magic.h
nsFSMagic = 0x6e736673
procFSMagic = 0x9fa0
procRootPath = "/proc"
nsDirPath = "ns"
taskDirPath = "task"
)
// NSType defines a namespace type.
type NSType string
@ -47,147 +24,8 @@ const (
NSTypeUTS NSType = "uts"
)
// CloneFlagsTable is exported so that consumers of this package don't need
// to define this same table again.
var CloneFlagsTable = make(map[NSType]int)
// Namespace describes a namespace that will be entered.
type Namespace struct {
Path string
PID int
Type NSType
}
type nsPair struct {
targetNS *os.File
threadNS *os.File
}
func init() {
var ns = map[NSType]int{
NSTypeCGroup: unix.CLONE_NEWCGROUP,
NSTypeIPC: unix.CLONE_NEWIPC,
NSTypeNet: unix.CLONE_NEWNET,
NSTypePID: unix.CLONE_NEWPID,
NSTypeUTS: unix.CLONE_NEWUTS,
}
for k, v := range ns {
if _, err := os.Stat(fmt.Sprint("/proc/self/ns/", string(k))); err == nil {
CloneFlagsTable[k] = v
}
}
}
func getNSPathFromPID(pid int, nsType NSType) string {
return filepath.Join(procRootPath, strconv.Itoa(pid), nsDirPath, string(nsType))
}
func getCurrentThreadNSPath(nsType NSType) string {
return filepath.Join(procRootPath, strconv.Itoa(os.Getpid()),
taskDirPath, strconv.Itoa(unix.Gettid()), nsDirPath, string(nsType))
}
func setNS(nsFile *os.File, nsType NSType) error {
if nsFile == nil {
return fmt.Errorf("File handler cannot be nil")
}
nsFlag, exist := CloneFlagsTable[nsType]
if !exist {
return fmt.Errorf("Unknown namespace type %q", nsType)
}
if err := unix.Setns(int(nsFile.Fd()), nsFlag); err != nil {
return fmt.Errorf("Error switching to ns %v: %v", nsFile.Name(), err)
}
return nil
}
// getFileFromNS checks the provided file path actually matches a real
// namespace filesystem, and then opens it to return a handler to this
// file. This is needed since the system call setns() expects a file
// descriptor to enter the given namespace.
func getFileFromNS(nsPath string) (*os.File, error) {
stat := syscall.Statfs_t{}
if err := syscall.Statfs(nsPath, &stat); err != nil {
return nil, fmt.Errorf("failed to Statfs %q: %v", nsPath, err)
}
switch stat.Type {
case nsFSMagic, procFSMagic:
break
default:
return nil, fmt.Errorf("unknown FS magic on %q: %x", nsPath, stat.Type)
}
file, err := os.Open(nsPath)
if err != nil {
return nil, err
}
return file, nil
}
// NsEnter executes the passed closure under the given namespace,
// restoring the original namespace afterwards.
func NsEnter(nsList []Namespace, toRun func() error) error {
targetNSList := make(map[NSType]*nsPair)
// Open all targeted namespaces.
for _, ns := range nsList {
targetNSPath := ns.Path
if targetNSPath == "" {
targetNSPath = getNSPathFromPID(ns.PID, ns.Type)
}
targetNS, err := getFileFromNS(targetNSPath)
if err != nil {
return fmt.Errorf("failed to open target ns: %v", err)
}
defer targetNS.Close()
targetNSList[ns.Type] = &nsPair{
targetNS: targetNS,
}
}
containedCall := func() error {
for nsType := range targetNSList {
threadNS, err := getFileFromNS(getCurrentThreadNSPath(nsType))
if err != nil {
return fmt.Errorf("failed to open current ns: %v", err)
}
defer threadNS.Close()
targetNSList[nsType].threadNS = threadNS
}
// Switch to namespaces all at once.
for nsType, pair := range targetNSList {
// Switch to targeted namespace.
if err := setNS(pair.targetNS, nsType); err != nil {
return fmt.Errorf("error switching to ns %v: %v", pair.targetNS.Name(), err)
}
// Switch back to initial namespace after closure return.
defer setNS(pair.threadNS, nsType)
}
return toRun()
}
var wg sync.WaitGroup
wg.Add(1)
var innerError error
go func() {
defer wg.Done()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
innerError = containedCall()
}()
wg.Wait()
return innerError
}

View File

@ -1,240 +0,0 @@
// 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"
ktu "github.com/kata-containers/kata-containers/src/runtime/pkg/katatestutils"
)
const testPID = 12345
var tu = ktu.NewTestConstraint(true)
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")
assert.NoError(t, err)
nsFilePath := nsFile.Name()
nsFile.Close()
assert.NoError(t, os.Remove(nsFilePath))
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")
assert.NoError(t, 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")
assert.NoError(t, 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) {
if tu.NotValid(ktu.NeedRoot()) {
t.Skip(ktu.TestDisabledNeedRoot)
}
nsList := supportedNamespaces()
sleepDuration := 60
cloneFlags := 0
for _, ns := range nsList {
cloneFlags |= CloneFlagsTable[ns.Type]
}
sleepPID, err := startSleepBinary(sleepDuration, cloneFlags)
assert.NoError(t, 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)
}
}