cli: refactor to align with katautils package

refactor the cli codes which can be shared with shimv2.

Signed-off-by: fupan <lifupan@gmail.com>
Signed-off-by: Eric Ernst <eric.ernst@intel.com>
This commit is contained in:
fupan 2018-11-09 11:16:17 +08:00
parent 780cd5f7b9
commit f0cb0c7ef7
45 changed files with 1677 additions and 1203 deletions

View File

@ -288,9 +288,6 @@ const project = "$(PROJECT_NAME)"
// prefix used to denote non-standard CLI commands and options. // prefix used to denote non-standard CLI commands and options.
const projectPrefix = "$(PROJECT_TYPE)" const projectPrefix = "$(PROJECT_TYPE)"
// systemdUnitName is the systemd(1) target used to launch the agent.
const systemdUnitName = "$(PROJECT_TAG).target"
// original URL for this project // original URL for this project
const projectURL = "$(PROJECT_URL)" const projectURL = "$(PROJECT_URL)"

View File

@ -11,10 +11,9 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"strings"
"github.com/kata-containers/runtime/pkg/katautils"
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
vf "github.com/kata-containers/runtime/virtcontainers/factory"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci" "github.com/kata-containers/runtime/virtcontainers/pkg/oci"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -87,44 +86,11 @@ var createCLICommand = cli.Command{
}, },
} }
// Use a variable to allow tests to modify its value
var getKernelParamsFunc = getKernelParams
func handleFactory(ctx context.Context, runtimeConfig oci.RuntimeConfig) {
if !runtimeConfig.FactoryConfig.Template {
return
}
factoryConfig := vf.Config{
Template: true,
VMConfig: vc.VMConfig{
HypervisorType: runtimeConfig.HypervisorType,
HypervisorConfig: runtimeConfig.HypervisorConfig,
AgentType: runtimeConfig.AgentType,
AgentConfig: runtimeConfig.AgentConfig,
},
}
kataLog.WithField("factory", factoryConfig).Info("load vm factory")
f, err := vf.NewFactory(ctx, factoryConfig, true)
if err != nil {
kataLog.WithError(err).Warn("load vm factory failed, about to create new one")
f, err = vf.NewFactory(ctx, factoryConfig, false)
if err != nil {
kataLog.WithError(err).Warn("create vm factory failed")
return
}
}
vci.SetFactory(ctx, f)
}
func create(ctx context.Context, containerID, bundlePath, console, pidFilePath string, detach, systemdCgroup bool, func create(ctx context.Context, containerID, bundlePath, console, pidFilePath string, detach, systemdCgroup bool,
runtimeConfig oci.RuntimeConfig) error { runtimeConfig oci.RuntimeConfig) error {
var err error var err error
span, ctx := trace(ctx, "create") span, ctx := katautils.Trace(ctx, "create")
defer span.Finish() defer span.Finish()
kataLog = kataLog.WithField("container", containerID) kataLog = kataLog.WithField("container", containerID)
@ -157,19 +123,19 @@ func create(ctx context.Context, containerID, bundlePath, console, pidFilePath s
return err return err
} }
handleFactory(ctx, runtimeConfig) katautils.HandleFactory(ctx, vci, &runtimeConfig)
disableOutput := noNeedForOutput(detach, ociSpec.Process.Terminal) disableOutput := noNeedForOutput(detach, ociSpec.Process.Terminal)
var process vc.Process var process vc.Process
switch containerType { switch containerType {
case vc.PodSandbox: case vc.PodSandbox:
process, err = createSandbox(ctx, ociSpec, runtimeConfig, containerID, bundlePath, console, disableOutput, systemdCgroup) _, process, err = katautils.CreateSandbox(ctx, vci, ociSpec, runtimeConfig, containerID, bundlePath, console, disableOutput, systemdCgroup, false)
if err != nil { if err != nil {
return err return err
} }
case vc.PodContainer: case vc.PodContainer:
process, err = createContainer(ctx, ociSpec, containerID, bundlePath, console, disableOutput) process, err = katautils.CreateContainer(ctx, vci, nil, ociSpec, containerID, bundlePath, console, disableOutput, false)
if err != nil { if err != nil {
return err return err
} }
@ -181,184 +147,8 @@ func create(ctx context.Context, containerID, bundlePath, console, pidFilePath s
return createPIDFile(ctx, pidFilePath, process.Pid) return createPIDFile(ctx, pidFilePath, process.Pid)
} }
var systemdKernelParam = []vc.Param{
{
Key: "init",
Value: "/usr/lib/systemd/systemd",
},
{
Key: "systemd.unit",
Value: systemdUnitName,
},
{
Key: "systemd.mask",
Value: "systemd-networkd.service",
},
{
Key: "systemd.mask",
Value: "systemd-networkd.socket",
},
}
func getKernelParams(needSystemd bool) []vc.Param {
p := []vc.Param{}
if needSystemd {
p = append(p, systemdKernelParam...)
}
return p
}
func needSystemd(config vc.HypervisorConfig) bool {
return config.ImagePath != ""
}
// setKernelParams adds the user-specified kernel parameters (from the
// configuration file) to the defaults so that the former take priority.
func setKernelParams(containerID string, runtimeConfig *oci.RuntimeConfig) error {
defaultKernelParams := getKernelParamsFunc(needSystemd(runtimeConfig.HypervisorConfig))
if runtimeConfig.HypervisorConfig.Debug {
strParams := vc.SerializeParams(defaultKernelParams, "=")
formatted := strings.Join(strParams, " ")
kataLog.WithField("default-kernel-parameters", formatted).Debug()
}
// retrieve the parameters specified in the config file
userKernelParams := runtimeConfig.HypervisorConfig.KernelParams
// reset
runtimeConfig.HypervisorConfig.KernelParams = []vc.Param{}
// first, add default values
for _, p := range defaultKernelParams {
if err := (runtimeConfig).AddKernelParam(p); err != nil {
return err
}
}
// now re-add the user-specified values so that they take priority.
for _, p := range userKernelParams {
if err := (runtimeConfig).AddKernelParam(p); err != nil {
return err
}
}
return nil
}
func createSandbox(ctx context.Context, ociSpec oci.CompatOCISpec, runtimeConfig oci.RuntimeConfig,
containerID, bundlePath, console string, disableOutput, systemdCgroup bool) (vc.Process, error) {
span, ctx := trace(ctx, "createSandbox")
defer span.Finish()
err := setKernelParams(containerID, &runtimeConfig)
if err != nil {
return vc.Process{}, err
}
sandboxConfig, err := oci.SandboxConfig(ociSpec, runtimeConfig, bundlePath, containerID, console, disableOutput, systemdCgroup)
if err != nil {
return vc.Process{}, err
}
// Important to create the network namespace before the sandbox is
// created, because it is not responsible for the creation of the
// netns if it does not exist.
if err := setupNetworkNamespace(&sandboxConfig.NetworkConfig); err != nil {
return vc.Process{}, err
}
// Run pre-start OCI hooks.
err = enterNetNS(sandboxConfig.NetworkConfig.NetNSPath, func() error {
return preStartHooks(ctx, ociSpec, containerID, bundlePath)
})
if err != nil {
return vc.Process{}, err
}
sandbox, err := vci.CreateSandbox(ctx, sandboxConfig)
if err != nil {
return vc.Process{}, err
}
sid := sandbox.ID()
kataLog = kataLog.WithField("sandbox", sid)
setExternalLoggers(ctx, kataLog)
span.SetTag("sandbox", sid)
containers := sandbox.GetAllContainers()
if len(containers) != 1 {
return vc.Process{}, fmt.Errorf("BUG: Container list from sandbox is wrong, expecting only one container, found %d containers", len(containers))
}
if err := addContainerIDMapping(ctx, containerID, sandbox.ID()); err != nil {
return vc.Process{}, err
}
return containers[0].Process(), nil
}
// setEphemeralStorageType sets the mount type to 'ephemeral'
// if the mount source path is provisioned by k8s for ephemeral storage.
// For the given pod ephemeral volume is created only once
// backed by tmpfs inside the VM. For successive containers
// of the same pod the already existing volume is reused.
func setEphemeralStorageType(ociSpec oci.CompatOCISpec) oci.CompatOCISpec {
for idx, mnt := range ociSpec.Mounts {
if IsEphemeralStorage(mnt.Source) {
ociSpec.Mounts[idx].Type = "ephemeral"
}
}
return ociSpec
}
func createContainer(ctx context.Context, ociSpec oci.CompatOCISpec, containerID, bundlePath,
console string, disableOutput bool) (vc.Process, error) {
span, ctx := trace(ctx, "createContainer")
defer span.Finish()
ociSpec = setEphemeralStorageType(ociSpec)
contConfig, err := oci.ContainerConfig(ociSpec, bundlePath, containerID, console, disableOutput)
if err != nil {
return vc.Process{}, err
}
sandboxID, err := ociSpec.SandboxID()
if err != nil {
return vc.Process{}, err
}
kataLog = kataLog.WithField("sandbox", sandboxID)
setExternalLoggers(ctx, kataLog)
span.SetTag("sandbox", sandboxID)
s, c, err := vci.CreateContainer(ctx, sandboxID, contConfig)
if err != nil {
return vc.Process{}, err
}
// Run pre-start OCI hooks.
err = enterNetNS(s.GetNetNs(), func() error {
return preStartHooks(ctx, ociSpec, containerID, bundlePath)
})
if err != nil {
return vc.Process{}, err
}
if err := addContainerIDMapping(ctx, containerID, sandboxID); err != nil {
return vc.Process{}, err
}
return c.Process(), nil
}
func createPIDFile(ctx context.Context, pidFilePath string, pid int) error { func createPIDFile(ctx context.Context, pidFilePath string, pid int) error {
span, _ := trace(ctx, "createPIDFile") span, _ := katautils.Trace(ctx, "createPIDFile")
defer span.Finish() defer span.Finish()
if pidFilePath == "" { if pidFilePath == "" {

View File

@ -7,16 +7,15 @@ package main
import ( import (
"context" "context"
"errors"
"flag" "flag"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings"
"testing" "testing"
"github.com/kata-containers/runtime/pkg/katautils"
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci" "github.com/kata-containers/runtime/virtcontainers/pkg/oci"
"github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock"
@ -29,34 +28,14 @@ const (
testPID = 100 testPID = 100
testConsole = "/dev/pts/999" testConsole = "/dev/pts/999"
testContainerTypeAnnotation = "io.kubernetes.cri-o.ContainerType" testContainerTypeAnnotation = "io.kubernetes.cri-o.ContainerType"
testSandboxIDAnnotation = "io.kubernetes.cri-o.SandboxID"
testContainerTypeSandbox = "sandbox" testContainerTypeSandbox = "sandbox"
testContainerTypeContainer = "container" testContainerTypeContainer = "container"
) )
var testStrPID = fmt.Sprintf("%d", testPID) var (
testStrPID = fmt.Sprintf("%d", testPID)
// return the value of the *last* param with the specified key ctrsMapTreePath = "/var/run/kata-containers/containers-mapping"
func findLastParam(key string, params []vc.Param) (string, error) { )
if key == "" {
return "", errors.New("ERROR: need non-nil key")
}
l := len(params)
if l == 0 {
return "", errors.New("ERROR: no params")
}
for i := l - 1; i >= 0; i-- {
p := params[i]
if key == p.Key {
return p.Value, nil
}
}
return "", fmt.Errorf("no param called %q found", name)
}
func TestCreatePIDFileSuccessful(t *testing.T) { func TestCreatePIDFileSuccessful(t *testing.T) {
pidDirPath, err := ioutil.TempDir(testDir, "pid-path-") pidDirPath, err := ioutil.TempDir(testDir, "pid-path-")
@ -230,6 +209,7 @@ func TestCreateInvalidArgs(t *testing.T) {
assert.NoError(err) assert.NoError(err)
defer os.RemoveAll(path) defer os.RemoveAll(path)
ctrsMapTreePath = path ctrsMapTreePath = path
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
defer func() { defer func() {
testingImpl.CreateSandboxFunc = nil testingImpl.CreateSandboxFunc = nil
@ -280,6 +260,7 @@ func TestCreateInvalidConfigJSON(t *testing.T) {
assert.NoError(err) assert.NoError(err)
defer os.RemoveAll(path) defer os.RemoveAll(path)
ctrsMapTreePath = path ctrsMapTreePath = path
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
tmpdir, err := ioutil.TempDir("", "") tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err) assert.NoError(err)
@ -296,7 +277,7 @@ func TestCreateInvalidConfigJSON(t *testing.T) {
pidFilePath := filepath.Join(tmpdir, "pidfile.txt") pidFilePath := filepath.Join(tmpdir, "pidfile.txt")
ociConfigFile := filepath.Join(bundlePath, "config.json") ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(fileExists(ociConfigFile)) assert.True(katautils.FileExists(ociConfigFile))
f, err := os.OpenFile(ociConfigFile, os.O_APPEND|os.O_WRONLY, testFileMode) f, err := os.OpenFile(ociConfigFile, os.O_APPEND|os.O_WRONLY, testFileMode)
assert.NoError(err) assert.NoError(err)
@ -321,6 +302,7 @@ func TestCreateInvalidContainerType(t *testing.T) {
assert.NoError(err) assert.NoError(err)
defer os.RemoveAll(path) defer os.RemoveAll(path)
ctrsMapTreePath = path ctrsMapTreePath = path
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
tmpdir, err := ioutil.TempDir("", "") tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err) assert.NoError(err)
@ -337,7 +319,7 @@ func TestCreateInvalidContainerType(t *testing.T) {
pidFilePath := filepath.Join(tmpdir, "pidfile.txt") pidFilePath := filepath.Join(tmpdir, "pidfile.txt")
ociConfigFile := filepath.Join(bundlePath, "config.json") ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(fileExists(ociConfigFile)) assert.True(katautils.FileExists(ociConfigFile))
spec, err := readOCIConfigFile(ociConfigFile) spec, err := readOCIConfigFile(ociConfigFile)
assert.NoError(err) assert.NoError(err)
@ -365,6 +347,7 @@ func TestCreateContainerInvalid(t *testing.T) {
assert.NoError(err) assert.NoError(err)
defer os.RemoveAll(path) defer os.RemoveAll(path)
ctrsMapTreePath = path ctrsMapTreePath = path
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
tmpdir, err := ioutil.TempDir("", "") tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err) assert.NoError(err)
@ -381,7 +364,7 @@ func TestCreateContainerInvalid(t *testing.T) {
pidFilePath := filepath.Join(tmpdir, "pidfile.txt") pidFilePath := filepath.Join(tmpdir, "pidfile.txt")
ociConfigFile := filepath.Join(bundlePath, "config.json") ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(fileExists(ociConfigFile)) assert.True(katautils.FileExists(ociConfigFile))
spec, err := readOCIConfigFile(ociConfigFile) spec, err := readOCIConfigFile(ociConfigFile)
@ -421,6 +404,7 @@ func TestCreateProcessCgroupsPathSuccessful(t *testing.T) {
assert.NoError(err) assert.NoError(err)
defer os.RemoveAll(path) defer os.RemoveAll(path)
ctrsMapTreePath = path ctrsMapTreePath = path
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) { testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) {
return sandbox, nil return sandbox, nil
@ -445,7 +429,7 @@ func TestCreateProcessCgroupsPathSuccessful(t *testing.T) {
pidFilePath := filepath.Join(tmpdir, "pidfile.txt") pidFilePath := filepath.Join(tmpdir, "pidfile.txt")
ociConfigFile := filepath.Join(bundlePath, "config.json") ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(fileExists(ociConfigFile)) assert.True(katautils.FileExists(ociConfigFile))
spec, err := readOCIConfigFile(ociConfigFile) spec, err := readOCIConfigFile(ociConfigFile)
assert.NoError(err) assert.NoError(err)
@ -523,6 +507,7 @@ func TestCreateCreateCgroupsFilesFail(t *testing.T) {
assert.NoError(err) assert.NoError(err)
defer os.RemoveAll(path) defer os.RemoveAll(path)
ctrsMapTreePath = path ctrsMapTreePath = path
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) { testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) {
return sandbox, nil return sandbox, nil
@ -547,7 +532,7 @@ func TestCreateCreateCgroupsFilesFail(t *testing.T) {
pidFilePath := filepath.Join(tmpdir, "pidfile.txt") pidFilePath := filepath.Join(tmpdir, "pidfile.txt")
ociConfigFile := filepath.Join(bundlePath, "config.json") ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(fileExists(ociConfigFile)) assert.True(katautils.FileExists(ociConfigFile))
spec, err := readOCIConfigFile(ociConfigFile) spec, err := readOCIConfigFile(ociConfigFile)
assert.NoError(err) assert.NoError(err)
@ -608,6 +593,7 @@ func TestCreateCreateCreatePidFileFail(t *testing.T) {
assert.NoError(err) assert.NoError(err)
defer os.RemoveAll(path) defer os.RemoveAll(path)
ctrsMapTreePath = path ctrsMapTreePath = path
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) { testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) {
return sandbox, nil return sandbox, nil
@ -633,7 +619,7 @@ func TestCreateCreateCreatePidFileFail(t *testing.T) {
pidFilePath := filepath.Join(pidDir, "pidfile.txt") pidFilePath := filepath.Join(pidDir, "pidfile.txt")
ociConfigFile := filepath.Join(bundlePath, "config.json") ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(fileExists(ociConfigFile)) assert.True(katautils.FileExists(ociConfigFile))
spec, err := readOCIConfigFile(ociConfigFile) spec, err := readOCIConfigFile(ociConfigFile)
assert.NoError(err) assert.NoError(err)
@ -683,6 +669,7 @@ func TestCreate(t *testing.T) {
assert.NoError(err) assert.NoError(err)
defer os.RemoveAll(path) defer os.RemoveAll(path)
ctrsMapTreePath = path ctrsMapTreePath = path
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) { testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) {
return sandbox, nil return sandbox, nil
@ -707,7 +694,7 @@ func TestCreate(t *testing.T) {
pidFilePath := filepath.Join(tmpdir, "pidfile.txt") pidFilePath := filepath.Join(tmpdir, "pidfile.txt")
ociConfigFile := filepath.Join(bundlePath, "config.json") ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(fileExists(ociConfigFile)) assert.True(katautils.FileExists(ociConfigFile))
spec, err := readOCIConfigFile(ociConfigFile) spec, err := readOCIConfigFile(ociConfigFile)
assert.NoError(err) assert.NoError(err)
@ -741,6 +728,7 @@ func TestCreateInvalidKernelParams(t *testing.T) {
assert.NoError(err) assert.NoError(err)
defer os.RemoveAll(path) defer os.RemoveAll(path)
ctrsMapTreePath = path ctrsMapTreePath = path
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
tmpdir, err := ioutil.TempDir("", "") tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err) assert.NoError(err)
@ -757,7 +745,7 @@ func TestCreateInvalidKernelParams(t *testing.T) {
pidFilePath := filepath.Join(tmpdir, "pidfile.txt") pidFilePath := filepath.Join(tmpdir, "pidfile.txt")
ociConfigFile := filepath.Join(bundlePath, "config.json") ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(fileExists(ociConfigFile)) assert.True(katautils.FileExists(ociConfigFile))
spec, err := readOCIConfigFile(ociConfigFile) spec, err := readOCIConfigFile(ociConfigFile)
assert.NoError(err) assert.NoError(err)
@ -770,12 +758,12 @@ func TestCreateInvalidKernelParams(t *testing.T) {
err = writeOCIConfigFile(spec, ociConfigFile) err = writeOCIConfigFile(spec, ociConfigFile)
assert.NoError(err) assert.NoError(err)
savedFunc := getKernelParamsFunc savedFunc := katautils.GetKernelParamsFunc
defer func() { defer func() {
getKernelParamsFunc = savedFunc katautils.GetKernelParamsFunc = savedFunc
}() }()
getKernelParamsFunc = func(needSystemd bool) []vc.Param { katautils.GetKernelParamsFunc = func(needSystemd bool) []vc.Param {
return []vc.Param{ return []vc.Param{
{ {
Key: "", Key: "",
@ -791,281 +779,3 @@ func TestCreateInvalidKernelParams(t *testing.T) {
os.RemoveAll(path) os.RemoveAll(path)
} }
} }
func TestCreateSandboxConfigFail(t *testing.T) {
assert := assert.New(t)
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(fileExists(ociConfigFile))
spec, err := readOCIConfigFile(ociConfigFile)
assert.NoError(err)
quota := int64(0)
limit := int64(0)
spec.Linux.Resources.Memory = &specs.LinuxMemory{
Limit: &limit,
}
spec.Linux.Resources.CPU = &specs.LinuxCPU{
// specify an invalid value
Quota: &quota,
}
_, err = createSandbox(context.Background(), spec, runtimeConfig, testContainerID, bundlePath, testConsole, true, true)
assert.Error(err)
}
func TestCreateCreateSandboxFail(t *testing.T) {
if os.Geteuid() != 0 {
t.Skip(testDisabledNeedNonRoot)
}
assert := assert.New(t)
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(fileExists(ociConfigFile))
spec, err := readOCIConfigFile(ociConfigFile)
assert.NoError(err)
_, err = createSandbox(context.Background(), spec, runtimeConfig, testContainerID, bundlePath, testConsole, true, true)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
}
func TestCreateCreateContainerContainerConfigFail(t *testing.T) {
assert := assert.New(t)
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(fileExists(ociConfigFile))
spec, err := readOCIConfigFile(ociConfigFile)
assert.NoError(err)
// Set invalid container type
containerType := "你好,世界"
spec.Annotations = make(map[string]string)
spec.Annotations[testContainerTypeAnnotation] = containerType
// rewrite file
err = writeOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
for _, disableOutput := range []bool{true, false} {
_, err = createContainer(context.Background(), spec, testContainerID, bundlePath, testConsole, disableOutput)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
assert.True(strings.Contains(err.Error(), containerType))
os.RemoveAll(path)
}
}
func TestCreateCreateContainerFail(t *testing.T) {
assert := assert.New(t)
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(fileExists(ociConfigFile))
spec, err := readOCIConfigFile(ociConfigFile)
assert.NoError(err)
// set expected container type and sandboxID
spec.Annotations = make(map[string]string)
spec.Annotations[testContainerTypeAnnotation] = testContainerTypeContainer
spec.Annotations[testSandboxIDAnnotation] = testSandboxID
// rewrite file
err = writeOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
for _, disableOutput := range []bool{true, false} {
_, err = createContainer(context.Background(), spec, testContainerID, bundlePath, testConsole, disableOutput)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
os.RemoveAll(path)
}
}
func TestSetEphemeralStorageType(t *testing.T) {
assert := assert.New(t)
ociSpec := oci.CompatOCISpec{}
var ociMounts []specs.Mount
mount := specs.Mount{
Source: "/var/lib/kubelet/pods/366c3a77-4869-11e8-b479-507b9ddd5ce4/volumes/kubernetes.io~empty-dir/cache-volume",
}
ociMounts = append(ociMounts, mount)
ociSpec.Mounts = ociMounts
ociSpec = setEphemeralStorageType(ociSpec)
mountType := ociSpec.Mounts[0].Type
assert.Equal(mountType, "ephemeral",
"Unexpected mount type, got %s expected ephemeral", mountType)
}
func TestCreateCreateContainer(t *testing.T) {
assert := assert.New(t)
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
testingImpl.CreateContainerFunc = func(ctx context.Context, sandboxID string, containerConfig vc.ContainerConfig) (vc.VCSandbox, vc.VCContainer, error) {
return &vcmock.Sandbox{}, &vcmock.Container{}, nil
}
defer func() {
testingImpl.CreateContainerFunc = nil
}()
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(fileExists(ociConfigFile))
spec, err := readOCIConfigFile(ociConfigFile)
assert.NoError(err)
// set expected container type and sandboxID
spec.Annotations = make(map[string]string)
spec.Annotations[testContainerTypeAnnotation] = testContainerTypeContainer
spec.Annotations[testSandboxIDAnnotation] = testSandboxID
// rewrite file
err = writeOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
for _, disableOutput := range []bool{true, false} {
_, err = createContainer(context.Background(), spec, testContainerID, bundlePath, testConsole, disableOutput)
assert.NoError(err)
os.RemoveAll(path)
}
}
func TestSetKernelParams(t *testing.T) {
assert := assert.New(t)
config := oci.RuntimeConfig{}
assert.Empty(config.HypervisorConfig.KernelParams)
err := setKernelParams(testContainerID, &config)
assert.NoError(err)
if needSystemd(config.HypervisorConfig) {
assert.NotEmpty(config.HypervisorConfig.KernelParams)
}
}
func TestSetKernelParamsUserOptionTakesPriority(t *testing.T) {
assert := assert.New(t)
initName := "init"
initValue := "/sbin/myinit"
ipName := "ip"
ipValue := "127.0.0.1"
params := []vc.Param{
{Key: initName, Value: initValue},
{Key: ipName, Value: ipValue},
}
hypervisorConfig := vc.HypervisorConfig{
KernelParams: params,
}
// Config containing user-specified kernel parameters
config := oci.RuntimeConfig{
HypervisorConfig: hypervisorConfig,
}
assert.NotEmpty(config.HypervisorConfig.KernelParams)
err := setKernelParams(testContainerID, &config)
assert.NoError(err)
kernelParams := config.HypervisorConfig.KernelParams
init, err := findLastParam(initName, kernelParams)
assert.NoError(err)
assert.Equal(initValue, init)
ip, err := findLastParam(ipName, kernelParams)
assert.NoError(err)
assert.Equal(ipValue, ip)
}

View File

@ -11,6 +11,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/kata-containers/runtime/pkg/katautils"
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
vcAnnot "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" vcAnnot "github.com/kata-containers/runtime/virtcontainers/pkg/annotations"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci" "github.com/kata-containers/runtime/virtcontainers/pkg/oci"
@ -60,7 +61,7 @@ EXAMPLE:
} }
func delete(ctx context.Context, containerID string, force bool) error { func delete(ctx context.Context, containerID string, force bool) error {
span, ctx := trace(ctx, "delete") span, ctx := katautils.Trace(ctx, "delete")
defer span.Finish() defer span.Finish()
kataLog = kataLog.WithField("container", containerID) kataLog = kataLog.WithField("container", containerID)
@ -119,15 +120,15 @@ func delete(ctx context.Context, containerID string, force bool) error {
} }
// Run post-stop OCI hooks. // Run post-stop OCI hooks.
if err := postStopHooks(ctx, ociSpec, sandboxID, status.Annotations[vcAnnot.BundlePathKey]); err != nil { if err := katautils.PostStopHooks(ctx, ociSpec, sandboxID, status.Annotations[vcAnnot.BundlePathKey]); err != nil {
return err return err
} }
return delContainerIDMapping(ctx, containerID) return katautils.DelContainerIDMapping(ctx, containerID)
} }
func deleteSandbox(ctx context.Context, sandboxID string) error { func deleteSandbox(ctx context.Context, sandboxID string) error {
span, _ := trace(ctx, "deleteSandbox") span, _ := katautils.Trace(ctx, "deleteSandbox")
defer span.Finish() defer span.Finish()
status, err := vci.StatusSandbox(ctx, sandboxID) status, err := vci.StatusSandbox(ctx, sandboxID)
@ -149,7 +150,7 @@ func deleteSandbox(ctx context.Context, sandboxID string) error {
} }
func deleteContainer(ctx context.Context, sandboxID, containerID string, forceStop bool) error { func deleteContainer(ctx context.Context, sandboxID, containerID string, forceStop bool) error {
span, _ := trace(ctx, "deleteContainer") span, _ := katautils.Trace(ctx, "deleteContainer")
defer span.Finish() defer span.Finish()
if forceStop { if forceStop {
@ -166,7 +167,7 @@ func deleteContainer(ctx context.Context, sandboxID, containerID string, forceSt
} }
func removeCgroupsPath(ctx context.Context, containerID string, cgroupsPathList []string) error { func removeCgroupsPath(ctx context.Context, containerID string, cgroupsPathList []string) error {
span, _ := trace(ctx, "removeCgroupsPath") span, _ := katautils.Trace(ctx, "removeCgroupsPath")
defer span.Finish() defer span.Finish()
if len(cgroupsPathList) == 0 { if len(cgroupsPathList) == 0 {

View File

@ -15,6 +15,7 @@ import (
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/kata-containers/runtime/pkg/katautils"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -141,7 +142,7 @@ information is displayed once every 5 seconds.`,
return err return err
} }
span, _ := trace(ctx, "events") span, _ := katautils.Trace(ctx, "events")
defer span.Finish() defer span.Finish()
containerID := context.Args().First() containerID := context.Args().First()

View File

@ -14,6 +14,7 @@ import (
"os" "os"
"syscall" "syscall"
"github.com/kata-containers/runtime/pkg/katautils"
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci" "github.com/kata-containers/runtime/virtcontainers/pkg/oci"
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
@ -188,7 +189,7 @@ func generateExecParams(context *cli.Context, specProcess *oci.CompatOCIProcess)
} }
func execute(ctx context.Context, context *cli.Context) error { func execute(ctx context.Context, context *cli.Context) error {
span, ctx := trace(ctx, "execute") span, ctx := katautils.Trace(ctx, "execute")
defer span.Finish() defer span.Finish()
containerID := context.Args().First() containerID := context.Args().First()

View File

@ -117,7 +117,7 @@ func getCPUFlags(cpuinfo string) string {
func haveKernelModule(module string) bool { func haveKernelModule(module string) bool {
// First, check to see if the module is already loaded // First, check to see if the module is already loaded
path := filepath.Join(sysModuleDir, module) path := filepath.Join(sysModuleDir, module)
if fileExists(path) { if katautils.FileExists(path) {
return true return true
} }
@ -288,7 +288,7 @@ var kataCheckCLICommand = cli.Command{
return err return err
} }
span, _ := trace(ctx, "kata-check") span, _ := katautils.Trace(ctx, "kata-check")
defer span.Finish() defer span.Finish()
setCPUtype() setCPUtype()

View File

@ -15,6 +15,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/kata-containers/runtime/pkg/katautils"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/urfave/cli" "github.com/urfave/cli"
@ -59,7 +60,7 @@ func createModules(assert *assert.Assertions, cpuInfoFile string, moduleData []t
} }
err = hostIsVMContainerCapable(details) err = hostIsVMContainerCapable(details)
if fileExists(cpuInfoFile) { if katautils.FileExists(cpuInfoFile) {
assert.NoError(err) assert.NoError(err)
} else { } else {
assert.Error(err) assert.Error(err)

View File

@ -14,6 +14,7 @@ import (
runtim "runtime" runtim "runtime"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"github.com/kata-containers/runtime/pkg/katautils"
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci" "github.com/kata-containers/runtime/virtcontainers/pkg/oci"
vcUtils "github.com/kata-containers/runtime/virtcontainers/utils" vcUtils "github.com/kata-containers/runtime/virtcontainers/utils"
@ -274,7 +275,7 @@ func getNetmonInfo(config oci.RuntimeConfig) (NetmonInfo, error) {
} }
func getCommandVersion(cmd string) (string, error) { func getCommandVersion(cmd string) (string, error) {
return runCommand([]string{cmd, "--version"}) return katautils.RunCommand([]string{cmd, "--version"})
} }
func getShimInfo(config oci.RuntimeConfig) (ShimInfo, error) { func getShimInfo(config oci.RuntimeConfig) (ShimInfo, error) {
@ -451,7 +452,7 @@ var kataEnvCLICommand = cli.Command{
return err return err
} }
span, _ := trace(ctx, "kata-env") span, _ := katautils.Trace(ctx, "kata-env")
defer span.Finish() defer span.Finish()
return handleSettings(defaultOutputFile, context) return handleSettings(defaultOutputFile, context)

View File

@ -193,7 +193,7 @@ func makeRuntimeConfig(prefixDir string) (configFile string, config oci.RuntimeC
return "", oci.RuntimeConfig{}, err return "", oci.RuntimeConfig{}, err
} }
_, config, _, err = katautils.LoadConfiguration(configFile, true, false) _, config, err = katautils.LoadConfiguration(configFile, true, false)
if err != nil { if err != nil {
return "", oci.RuntimeConfig{}, err return "", oci.RuntimeConfig{}, err
} }

View File

@ -12,6 +12,7 @@ import (
"strconv" "strconv"
"syscall" "syscall"
"github.com/kata-containers/runtime/pkg/katautils"
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci" "github.com/kata-containers/runtime/virtcontainers/pkg/oci"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -97,7 +98,7 @@ var signalList = map[string]syscall.Signal{
} }
func kill(ctx context.Context, containerID, signal string, all bool) error { func kill(ctx context.Context, containerID, signal string, all bool) error {
span, _ := trace(ctx, "kill") span, _ := katautils.Trace(ctx, "kill")
defer span.Finish() defer span.Finish()
kataLog = kataLog.WithField("container", containerID) kataLog = kataLog.WithField("container", containerID)

View File

@ -19,6 +19,7 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
"github.com/kata-containers/runtime/pkg/katautils"
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
oci "github.com/kata-containers/runtime/virtcontainers/pkg/oci" oci "github.com/kata-containers/runtime/virtcontainers/pkg/oci"
) )
@ -114,7 +115,7 @@ To list containers created using a non-default value for "--root":
return err return err
} }
span, ctx := trace(ctx, "list") span, ctx := katautils.Trace(ctx, "list")
defer span.Finish() defer span.Finish()
s, err := getContainers(ctx, context) s, err := getContainers(ctx, context)

View File

@ -60,9 +60,6 @@ var originalLoggerLevel logrus.Level
var debug = false var debug = false
// if true, enable opentracing support.
var tracing = false
// if true, coredump when an internal error occurs or a fatal signal is received // if true, coredump when an internal error occurs or a fatal signal is received
var crashOnError = false var crashOnError = false
@ -193,7 +190,7 @@ func setupSignalHandler(ctx context.Context) {
} }
dieCb := func() { dieCb := func() {
stopTracing(ctx) katautils.StopTracing(ctx)
} }
go func() { go func() {
@ -229,7 +226,7 @@ func setExternalLoggers(ctx context.Context, logger *logrus.Entry) {
// created. // created.
if opentracing.SpanFromContext(ctx) != nil { if opentracing.SpanFromContext(ctx) != nil {
span, ctx = trace(ctx, "setExternalLoggers") span, ctx = katautils.Trace(ctx, "setExternalLoggers")
defer span.Finish() defer span.Finish()
} }
@ -307,7 +304,7 @@ func beforeSubcommands(c *cli.Context) error {
katautils.SetConfigOptions(name, defaultRuntimeConfiguration, defaultSysConfRuntimeConfiguration) katautils.SetConfigOptions(name, defaultRuntimeConfiguration, defaultSysConfRuntimeConfiguration)
configFile, runtimeConfig, tracing, err = katautils.LoadConfiguration(c.GlobalString(configFilePathOption), ignoreLogging, false) configFile, runtimeConfig, err = katautils.LoadConfiguration(c.GlobalString(configFilePathOption), ignoreLogging, false)
if err != nil { if err != nil {
fatal(err) fatal(err)
} }
@ -360,7 +357,7 @@ func handleShowConfig(context *cli.Context) {
} }
func setupTracing(context *cli.Context, rootSpanName string) error { func setupTracing(context *cli.Context, rootSpanName string) error {
tracer, err := createTracer(name) tracer, err := katautils.CreateTracer(name)
if err != nil { if err != nil {
fatal(err) fatal(err)
} }
@ -397,7 +394,7 @@ func afterSubcommands(c *cli.Context) error {
return err return err
} }
stopTracing(ctx) katautils.StopTracing(ctx)
return nil return nil
} }
@ -546,7 +543,7 @@ func main() {
ctx := context.Background() ctx := context.Background()
dieCb := func() { dieCb := func() {
stopTracing(ctx) katautils.StopTracing(ctx)
} }
defer signals.HandlePanic(dieCb) defer signals.HandlePanic(dieCb)

View File

@ -33,7 +33,6 @@ import (
) )
const ( const (
testDisabledNeedRoot = "Test disabled as requires root user"
testDisabledNeedNonRoot = "Test disabled as requires non-root user" testDisabledNeedNonRoot = "Test disabled as requires non-root user"
testDirMode = os.FileMode(0750) testDirMode = os.FileMode(0750)
testFileMode = os.FileMode(0640) testFileMode = os.FileMode(0640)
@ -90,7 +89,7 @@ func init() {
fmt.Printf("INFO: test directory is %v\n", testDir) fmt.Printf("INFO: test directory is %v\n", testDir)
fmt.Printf("INFO: ensuring docker is running\n") fmt.Printf("INFO: ensuring docker is running\n")
output, err := runCommandFull([]string{"docker", "version"}, true) output, err := katautils.RunCommandFull([]string{"docker", "version"}, true)
if err != nil { if err != nil {
panic(fmt.Sprintf("ERROR: docker daemon is not installed, not running, or not accessible to current user: %v (error %v)", panic(fmt.Sprintf("ERROR: docker daemon is not installed, not running, or not accessible to current user: %v (error %v)",
output, err)) output, err))
@ -101,11 +100,11 @@ func init() {
fmt.Printf("INFO: ensuring required docker image (%v) is available\n", testDockerImage) fmt.Printf("INFO: ensuring required docker image (%v) is available\n", testDockerImage)
// Only hit the network if the image doesn't exist locally // Only hit the network if the image doesn't exist locally
_, err = runCommand([]string{"docker", "inspect", "--type=image", testDockerImage}) _, err = katautils.RunCommand([]string{"docker", "inspect", "--type=image", testDockerImage})
if err == nil { if err == nil {
fmt.Printf("INFO: docker image %v already exists locally\n", testDockerImage) fmt.Printf("INFO: docker image %v already exists locally\n", testDockerImage)
} else { } else {
_, err = runCommand([]string{"docker", "pull", testDockerImage}) _, err = katautils.RunCommand([]string{"docker", "pull", testDockerImage})
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -256,7 +255,7 @@ func createOCIConfig(bundleDir string) error {
return errors.New("BUG: Need bundle directory") return errors.New("BUG: Need bundle directory")
} }
if !fileExists(bundleDir) { if !katautils.FileExists(bundleDir) {
return fmt.Errorf("BUG: Bundle directory %s does not exist", bundleDir) return fmt.Errorf("BUG: Bundle directory %s does not exist", bundleDir)
} }
@ -276,13 +275,13 @@ func createOCIConfig(bundleDir string) error {
return errors.New("Cannot find command to generate OCI config file") return errors.New("Cannot find command to generate OCI config file")
} }
_, err := runCommand([]string{configCmd, "spec", "--bundle", bundleDir}) _, err := katautils.RunCommand([]string{configCmd, "spec", "--bundle", bundleDir})
if err != nil { if err != nil {
return err return err
} }
specFile := filepath.Join(bundleDir, specConfig) specFile := filepath.Join(bundleDir, specConfig)
if !fileExists(specFile) { if !katautils.FileExists(specFile) {
return fmt.Errorf("generated OCI config file does not exist: %v", specFile) return fmt.Errorf("generated OCI config file does not exist: %v", specFile)
} }
@ -297,7 +296,7 @@ func createRootfs(dir string) error {
return err return err
} }
container, err := runCommand([]string{"docker", "create", testDockerImage}) container, err := katautils.RunCommand([]string{"docker", "create", testDockerImage})
if err != nil { if err != nil {
return err return err
} }
@ -328,7 +327,7 @@ func createRootfs(dir string) error {
} }
// Clean up // Clean up
_, err = runCommand([]string{"docker", "rm", container}) _, err = katautils.RunCommand([]string{"docker", "rm", container})
if err != nil { if err != nil {
return err return err
} }
@ -346,7 +345,7 @@ func realMakeOCIBundle(bundleDir string) error {
return errors.New("BUG: Need bundle directory") return errors.New("BUG: Need bundle directory")
} }
if !fileExists(bundleDir) { if !katautils.FileExists(bundleDir) {
return fmt.Errorf("BUG: Bundle directory %v does not exist", bundleDir) return fmt.Errorf("BUG: Bundle directory %v does not exist", bundleDir)
} }
@ -396,12 +395,12 @@ func makeOCIBundle(bundleDir string) error {
base := filepath.Dir(bundleDir) base := filepath.Dir(bundleDir)
for _, dir := range []string{from, base} { for _, dir := range []string{from, base} {
if !fileExists(dir) { if !katautils.FileExists(dir) {
return fmt.Errorf("BUG: directory %v should exist", dir) return fmt.Errorf("BUG: directory %v should exist", dir)
} }
} }
output, err := runCommandFull([]string{"cp", "-a", from, to}, true) output, err := katautils.RunCommandFull([]string{"cp", "-a", from, to}, true)
if err != nil { if err != nil {
return fmt.Errorf("failed to copy test OCI bundle from %v to %v: %v (output: %v)", from, to, err, output) return fmt.Errorf("failed to copy test OCI bundle from %v to %v: %v (output: %v)", from, to, err, output)
} }
@ -501,7 +500,7 @@ func TestMakeOCIBundle(t *testing.T) {
assert.NoError(err) assert.NoError(err)
specFile := filepath.Join(bundleDir, specConfig) specFile := filepath.Join(bundleDir, specConfig)
assert.True(fileExists(specFile)) assert.True(katautils.FileExists(specFile))
} }
func TestCreateOCIConfig(t *testing.T) { func TestCreateOCIConfig(t *testing.T) {
@ -524,7 +523,7 @@ func TestCreateOCIConfig(t *testing.T) {
assert.NoError(err) assert.NoError(err)
specFile := filepath.Join(bundleDir, specConfig) specFile := filepath.Join(bundleDir, specConfig)
assert.True(fileExists(specFile)) assert.True(katautils.FileExists(specFile))
} }
func TestCreateRootfs(t *testing.T) { func TestCreateRootfs(t *testing.T) {
@ -535,7 +534,7 @@ func TestCreateRootfs(t *testing.T) {
defer os.RemoveAll(tmpdir) defer os.RemoveAll(tmpdir)
rootfsDir := filepath.Join(tmpdir, "rootfs") rootfsDir := filepath.Join(tmpdir, "rootfs")
assert.False(fileExists(rootfsDir)) assert.False(katautils.FileExists(rootfsDir))
err = createRootfs(rootfsDir) err = createRootfs(rootfsDir)
assert.NoError(err) assert.NoError(err)
@ -543,11 +542,11 @@ func TestCreateRootfs(t *testing.T) {
// non-comprehensive list of expected directories // non-comprehensive list of expected directories
expectedDirs := []string{"bin", "dev", "etc", "usr", "var"} expectedDirs := []string{"bin", "dev", "etc", "usr", "var"}
assert.True(fileExists(rootfsDir)) assert.True(katautils.FileExists(rootfsDir))
for _, dir := range expectedDirs { for _, dir := range expectedDirs {
dirPath := filepath.Join(rootfsDir, dir) dirPath := filepath.Join(rootfsDir, dir)
assert.True(fileExists(dirPath)) assert.True(katautils.FileExists(dirPath))
} }
} }
@ -1122,5 +1121,6 @@ func createTempContainerIDMapping(containerID, sandboxID string) (string, error)
return "", err return "", err
} }
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
return tmpDir, nil return tmpDir, nil
} }

View File

@ -6,17 +6,11 @@
package main package main
import ( import (
"bufio"
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
"path/filepath"
"strings"
"golang.org/x/sys/unix"
"github.com/containernetworking/plugins/pkg/ns"
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/kata-containers/runtime/virtcontainers/pkg/types" "github.com/kata-containers/runtime/virtcontainers/pkg/types"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -233,133 +227,3 @@ func networkListCommand(ctx context.Context, containerID string, opType networkT
} }
return err return err
} }
const procMountInfoFile = "/proc/self/mountinfo"
// getNetNsFromBindMount returns the network namespace for the bind-mounted path
func getNetNsFromBindMount(nsPath string, procMountFile string) (string, error) {
netNsMountType := "nsfs"
// Resolve all symlinks in the path as the mountinfo file contains
// resolved paths.
nsPath, err := filepath.EvalSymlinks(nsPath)
if err != nil {
return "", err
}
f, err := os.Open(procMountFile)
if err != nil {
return "", err
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
text := scanner.Text()
// Scan the mountinfo file to search for the network namespace path
// This file contains mounts in the eg format:
// "711 26 0:3 net:[4026532009] /run/docker/netns/default rw shared:535 - nsfs nsfs rw"
//
// Reference: https://www.kernel.org/doc/Documentation/filesystems/proc.txt
// We are interested in the first 9 fields of this file,
// to check for the correct mount type.
fields := strings.Split(text, " ")
if len(fields) < 9 {
continue
}
// We check here if the mount type is a network namespace mount type, namely "nsfs"
mountTypeFieldIdx := 8
if fields[mountTypeFieldIdx] != netNsMountType {
continue
}
// This is the mount point/destination for the mount
mntDestIdx := 4
if fields[mntDestIdx] != nsPath {
continue
}
// This is the root/source of the mount
return fields[3], nil
}
return "", nil
}
// hostNetworkingRequested checks if the network namespace requested is the
// same as the current process.
func hostNetworkingRequested(configNetNs string) (bool, error) {
var evalNS, nsPath, currentNsPath string
var err error
// Net namespace provided as "/proc/pid/ns/net" or "/proc/<pid>/task/<tid>/ns/net"
if strings.HasPrefix(configNetNs, "/proc") && strings.HasSuffix(configNetNs, "/ns/net") {
if _, err := os.Stat(configNetNs); err != nil {
return false, err
}
// 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.
if _, err = filepath.EvalSymlinks(configNetNs); err != nil {
nsPath = err.(*os.PathError).Path
} else {
return false, fmt.Errorf("Net namespace path %s is not a symlink", configNetNs)
}
_, evalNS = filepath.Split(nsPath)
} else {
// Bind-mounted path provided
evalNS, _ = getNetNsFromBindMount(configNetNs, procMountInfoFile)
}
currentNS := fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid())
if _, err = filepath.EvalSymlinks(currentNS); err != nil {
currentNsPath = err.(*os.PathError).Path
} else {
return false, fmt.Errorf("Unexpected: Current network namespace path is not a symlink")
}
_, evalCurrentNS := filepath.Split(currentNsPath)
if evalNS == evalCurrentNS {
return true, nil
}
return false, nil
}
func setupNetworkNamespace(config *vc.NetworkConfig) error {
if config.DisableNewNetNs {
kataLog.Info("DisableNewNetNs is on, shim and hypervisor are running in the host netns")
return nil
}
if config.NetNSPath == "" {
n, err := ns.NewNS()
if err != nil {
return err
}
config.NetNSPath = n.Path()
config.NetNsCreated = true
return nil
}
isHostNs, err := hostNetworkingRequested(config.NetNSPath)
if err != nil {
return err
}
if isHostNs {
return fmt.Errorf("Host networking requested, not supported by runtime")
}
return nil
}

View File

@ -8,16 +8,10 @@ package main
import ( import (
"context" "context"
"flag" "flag"
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath"
"syscall"
"testing" "testing"
"golang.org/x/sys/unix"
"github.com/containernetworking/plugins/pkg/ns"
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/kata-containers/runtime/virtcontainers/pkg/types" "github.com/kata-containers/runtime/virtcontainers/pkg/types"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -93,133 +87,3 @@ func TestNetworkCliFunction(t *testing.T) {
f.Close() f.Close()
execCLICommandFunc(assert, updateRoutesCommand, set, false) execCLICommandFunc(assert, updateRoutesCommand, set, false)
} }
func TestGetNetNsFromBindMount(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
mountFile := filepath.Join(tmpdir, "mountInfo")
nsPath := filepath.Join(tmpdir, "ns123")
// Non-existent namespace path
_, err = getNetNsFromBindMount(nsPath, mountFile)
assert.NotNil(err)
tmpNSPath := filepath.Join(tmpdir, "testNetNs")
f, err := os.Create(tmpNSPath)
assert.NoError(err)
defer f.Close()
type testData struct {
contents string
expectedResult string
}
data := []testData{
{fmt.Sprintf("711 26 0:3 net:[4026532008] %s rw shared:535 - nsfs nsfs rw", tmpNSPath), "net:[4026532008]"},
{"711 26 0:3 net:[4026532008] /run/netns/ns123 rw shared:535 - tmpfs tmpfs rw", ""},
{"a a a a a a a - b c d", ""},
{"", ""},
}
for i, d := range data {
err := ioutil.WriteFile(mountFile, []byte(d.contents), 0640)
assert.NoError(err)
path, err := getNetNsFromBindMount(tmpNSPath, mountFile)
assert.NoError(err, fmt.Sprintf("got %q, test data: %+v", path, d))
assert.Equal(d.expectedResult, path, "Test %d, expected %s, got %s", i, d.expectedResult, path)
}
}
func TestHostNetworkingRequested(t *testing.T) {
assert := assert.New(t)
if os.Geteuid() != 0 {
t.Skip(testDisabledNeedRoot)
}
// Network namespace same as the host
selfNsPath := "/proc/self/ns/net"
isHostNs, err := hostNetworkingRequested(selfNsPath)
assert.NoError(err)
assert.True(isHostNs)
// Non-existent netns path
nsPath := "/proc/123456789/ns/net"
_, err = hostNetworkingRequested(nsPath)
assert.Error(err)
// Bind-mounted Netns
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
// Create a bind mount to the current network namespace.
tmpFile := filepath.Join(tmpdir, "testNetNs")
f, err := os.Create(tmpFile)
assert.NoError(err)
defer f.Close()
err = syscall.Mount(selfNsPath, tmpFile, "bind", syscall.MS_BIND, "")
assert.Nil(err)
isHostNs, err = hostNetworkingRequested(tmpFile)
assert.NoError(err)
assert.True(isHostNs)
syscall.Unmount(tmpFile, 0)
}
func TestSetupNetworkNamespace(t *testing.T) {
if os.Geteuid() != 0 {
t.Skip(testDisabledNeedNonRoot)
}
assert := assert.New(t)
// Network namespace same as the host
config := &vc.NetworkConfig{
NetNSPath: "/proc/self/ns/net",
}
err := setupNetworkNamespace(config)
assert.Error(err)
// Non-existent netns path
config = &vc.NetworkConfig{
NetNSPath: "/proc/123456789/ns/net",
}
err = setupNetworkNamespace(config)
assert.Error(err)
// Existent netns path
n, err := ns.NewNS()
assert.NoError(err)
config = &vc.NetworkConfig{
NetNSPath: n.Path(),
}
err = setupNetworkNamespace(config)
assert.NoError(err)
n.Close()
// Empty netns path
config = &vc.NetworkConfig{}
err = setupNetworkNamespace(config)
assert.NoError(err)
n, err = ns.GetNS(config.NetNSPath)
assert.NoError(err)
assert.NotNil(n)
assert.True(config.NetNsCreated)
n.Close()
unix.Unmount(config.NetNSPath, unix.MNT_DETACH)
os.RemoveAll(config.NetNSPath)
// Config with DisableNewNetNs
config = &vc.NetworkConfig{DisableNewNetNs: true}
err = setupNetworkNamespace(config)
assert.NoError(err)
}

View File

@ -9,15 +9,12 @@ import (
"bufio" "bufio"
"context" "context"
"fmt" "fmt"
"io/ioutil"
"net" "net"
"os" "os"
"path/filepath" "path/filepath"
goruntime "runtime"
"strings" "strings"
"syscall" "syscall"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/kata-containers/runtime/pkg/katautils" "github.com/kata-containers/runtime/pkg/katautils"
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/opencontainers/runc/libcontainer/utils" "github.com/opencontainers/runc/libcontainer/utils"
@ -25,8 +22,6 @@ import (
// Contants related to cgroup memory directory // Contants related to cgroup memory directory
const ( const (
ctrsMappingDirMode = os.FileMode(0750)
// Filesystem type corresponding to CGROUP_SUPER_MAGIC as listed // Filesystem type corresponding to CGROUP_SUPER_MAGIC as listed
// here: http://man7.org/linux/man-pages/man2/statfs.2.html // here: http://man7.org/linux/man-pages/man2/statfs.2.html
cgroupFsType = 0x27e0eb cgroupFsType = 0x27e0eb
@ -36,8 +31,6 @@ var cgroupsDirPath string
var procMountInfo = "/proc/self/mountinfo" var procMountInfo = "/proc/self/mountinfo"
var ctrsMapTreePath = "/var/run/kata-containers/containers-mapping"
// getContainerInfo returns the container status and its sandbox ID. // getContainerInfo returns the container status and its sandbox ID.
func getContainerInfo(ctx context.Context, containerID string) (vc.ContainerStatus, string, error) { func getContainerInfo(ctx context.Context, containerID string) (vc.ContainerStatus, string, error) {
// container ID MUST be provided. // container ID MUST be provided.
@ -45,7 +38,7 @@ func getContainerInfo(ctx context.Context, containerID string) (vc.ContainerStat
return vc.ContainerStatus{}, "", fmt.Errorf("Missing container ID") return vc.ContainerStatus{}, "", fmt.Errorf("Missing container ID")
} }
sandboxID, err := fetchContainerIDMapping(containerID) sandboxID, err := katautils.FetchContainerIDMapping(containerID)
if err != nil { if err != nil {
return vc.ContainerStatus{}, "", err return vc.ContainerStatus{}, "", err
} }
@ -218,100 +211,3 @@ func getCgroupsDirPath(mountInfoFile string) (string, error) {
return cgroupRootPath, nil return cgroupRootPath, nil
} }
// This function assumes it should find only one file inside the container
// ID directory. If there are several files, we could not determine which
// file name corresponds to the sandbox ID associated, and this would throw
// an error.
func fetchContainerIDMapping(containerID string) (string, error) {
if containerID == "" {
return "", fmt.Errorf("Missing container ID")
}
dirPath := filepath.Join(ctrsMapTreePath, containerID)
files, err := ioutil.ReadDir(dirPath)
if err != nil {
if os.IsNotExist(err) {
return "", nil
}
return "", err
}
if len(files) != 1 {
return "", fmt.Errorf("Too many files (%d) in %q", len(files), dirPath)
}
return files[0].Name(), nil
}
func addContainerIDMapping(ctx context.Context, containerID, sandboxID string) error {
span, _ := trace(ctx, "addContainerIDMapping")
defer span.Finish()
if containerID == "" {
return fmt.Errorf("Missing container ID")
}
if sandboxID == "" {
return fmt.Errorf("Missing sandbox ID")
}
parentPath := filepath.Join(ctrsMapTreePath, containerID)
if err := os.RemoveAll(parentPath); err != nil {
return err
}
path := filepath.Join(parentPath, sandboxID)
if err := os.MkdirAll(path, ctrsMappingDirMode); err != nil {
return err
}
return nil
}
func delContainerIDMapping(ctx context.Context, containerID string) error {
span, _ := trace(ctx, "delContainerIDMapping")
defer span.Finish()
if containerID == "" {
return fmt.Errorf("Missing container ID")
}
path := filepath.Join(ctrsMapTreePath, containerID)
return os.RemoveAll(path)
}
// enterNetNS is free from any call to a go routine, and it calls
// into runtime.LockOSThread(), meaning it won't be executed in a
// different thread than the one expected by the caller.
func enterNetNS(netNSPath string, cb func() error) error {
if netNSPath == "" {
return cb()
}
goruntime.LockOSThread()
defer goruntime.UnlockOSThread()
currentNS, err := ns.GetCurrentNS()
if err != nil {
return err
}
defer currentNS.Close()
targetNS, err := ns.GetNS(netNSPath)
if err != nil {
return err
}
if err := targetNS.Set(); err != nil {
return err
}
defer currentNS.Set()
return cb()
}

View File

@ -317,106 +317,3 @@ func TestGetCgroupsDirPath(t *testing.T) {
assert.Equal(d.expectedResult, path) assert.Equal(d.expectedResult, path)
} }
} }
func TestFetchContainerIDMappingContainerIDEmptyFailure(t *testing.T) {
assert := assert.New(t)
sandboxID, err := fetchContainerIDMapping("")
assert.Error(err)
assert.Empty(sandboxID)
}
func TestFetchContainerIDMappingEmptyMappingSuccess(t *testing.T) {
assert := assert.New(t)
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
sandboxID, err := fetchContainerIDMapping(testContainerID)
assert.NoError(err)
assert.Empty(sandboxID)
}
func TestFetchContainerIDMappingTooManyFilesFailure(t *testing.T) {
assert := assert.New(t)
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
err = os.MkdirAll(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID+"2"), ctrsMappingDirMode)
assert.NoError(err)
sandboxID, err := fetchContainerIDMapping(testContainerID)
assert.Error(err)
assert.Empty(sandboxID)
}
func TestFetchContainerIDMappingSuccess(t *testing.T) {
assert := assert.New(t)
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
sandboxID, err := fetchContainerIDMapping(testContainerID)
assert.NoError(err)
assert.Equal(sandboxID, testSandboxID)
}
func TestAddContainerIDMappingContainerIDEmptyFailure(t *testing.T) {
assert := assert.New(t)
err := addContainerIDMapping(context.Background(), "", testSandboxID)
assert.Error(err)
}
func TestAddContainerIDMappingSandboxIDEmptyFailure(t *testing.T) {
assert := assert.New(t)
err := addContainerIDMapping(context.Background(), testContainerID, "")
assert.Error(err)
}
func TestAddContainerIDMappingSuccess(t *testing.T) {
assert := assert.New(t)
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
_, err = os.Stat(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID))
assert.True(os.IsNotExist(err))
err = addContainerIDMapping(context.Background(), testContainerID, testSandboxID)
assert.NoError(err)
_, err = os.Stat(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID))
assert.NoError(err)
}
func TestDelContainerIDMappingContainerIDEmptyFailure(t *testing.T) {
assert := assert.New(t)
err := delContainerIDMapping(context.Background(), "")
assert.Error(err)
}
func TestDelContainerIDMappingSuccess(t *testing.T) {
assert := assert.New(t)
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
_, err = os.Stat(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID))
assert.NoError(err)
err = delContainerIDMapping(context.Background(), testContainerID)
assert.NoError(err)
_, err = os.Stat(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID))
assert.True(os.IsNotExist(err))
}

View File

@ -9,6 +9,7 @@ package main
import ( import (
"context" "context"
"github.com/kata-containers/runtime/pkg/katautils"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -57,7 +58,7 @@ func toggle(c *cli.Context, pause bool) error {
} }
func toggleContainerPause(ctx context.Context, containerID string, pause bool) (err error) { func toggleContainerPause(ctx context.Context, containerID string, pause bool) (err error) {
span, _ := trace(ctx, "pause") span, _ := katautils.Trace(ctx, "pause")
defer span.Finish() defer span.Finish()
span.SetTag("pause", pause) span.SetTag("pause", pause)

View File

@ -10,6 +10,7 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/kata-containers/runtime/pkg/katautils"
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
@ -50,7 +51,7 @@ var psCLICommand = cli.Command{
} }
func ps(ctx context.Context, containerID, format string, args []string) error { func ps(ctx context.Context, containerID, format string, args []string) error {
span, _ := trace(ctx, "ps") span, _ := katautils.Trace(ctx, "ps")
defer span.Finish() defer span.Finish()
if containerID == "" { if containerID == "" {

View File

@ -13,6 +13,7 @@ import (
"os" "os"
"syscall" "syscall"
"github.com/kata-containers/runtime/pkg/katautils"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci" "github.com/kata-containers/runtime/virtcontainers/pkg/oci"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -82,7 +83,7 @@ var runCLICommand = cli.Command{
func run(ctx context.Context, containerID, bundle, console, consoleSocket, pidFile string, detach, systemdCgroup bool, func run(ctx context.Context, containerID, bundle, console, consoleSocket, pidFile string, detach, systemdCgroup bool,
runtimeConfig oci.RuntimeConfig) error { runtimeConfig oci.RuntimeConfig) error {
span, ctx := trace(ctx, "run") span, ctx := katautils.Trace(ctx, "run")
defer span.Finish() defer span.Finish()
consolePath, err := setupConsole(console, consoleSocket) consolePath, err := setupConsole(console, consoleSocket)

View File

@ -12,6 +12,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"github.com/kata-containers/runtime/pkg/katautils"
"github.com/opencontainers/runc/libcontainer/specconv" "github.com/opencontainers/runc/libcontainer/specconv"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -77,7 +78,7 @@ generate a proper rootless spec file.`,
return err return err
} }
span, _ := trace(ctx, "spec") span, _ := katautils.Trace(ctx, "spec")
defer span.Finish() defer span.Finish()
spec := specconv.Example() spec := specconv.Example()

View File

@ -10,6 +10,7 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/kata-containers/runtime/pkg/katautils"
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
vcAnnot "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" vcAnnot "github.com/kata-containers/runtime/virtcontainers/pkg/annotations"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci" "github.com/kata-containers/runtime/virtcontainers/pkg/oci"
@ -48,7 +49,7 @@ var startCLICommand = cli.Command{
} }
func start(ctx context.Context, containerID string) (vc.VCSandbox, error) { func start(ctx context.Context, containerID string) (vc.VCSandbox, error) {
span, _ := trace(ctx, "start") span, _ := katautils.Trace(ctx, "start")
defer span.Finish() defer span.Finish()
kataLog = kataLog.WithField("container", containerID) kataLog = kataLog.WithField("container", containerID)
@ -101,8 +102,8 @@ func start(ctx context.Context, containerID string) (vc.VCSandbox, error) {
} }
// Run post-start OCI hooks. // Run post-start OCI hooks.
err = enterNetNS(sandbox.GetNetNs(), func() error { err = katautils.EnterNetNS(sandbox.GetNetNs(), func() error {
return postStartHooks(ctx, ociSpec, sandboxID, status.Annotations[vcAnnot.BundlePathKey]) return katautils.PostStartHooks(ctx, ociSpec, sandboxID, status.Annotations[vcAnnot.BundlePathKey])
}) })
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -13,6 +13,7 @@ import (
"os" "os"
"testing" "testing"
"github.com/kata-containers/runtime/pkg/katautils"
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci" "github.com/kata-containers/runtime/virtcontainers/pkg/oci"
@ -41,7 +42,7 @@ func TestStartInvalidArgs(t *testing.T) {
path, err = ioutil.TempDir("", "containers-mapping") path, err = ioutil.TempDir("", "containers-mapping")
assert.NoError(err) assert.NoError(err)
defer os.RemoveAll(path) defer os.RemoveAll(path)
ctrsMapTreePath = path katautils.SetCtrsMapTreePath(path)
// Container missing in container mapping // Container missing in container mapping
_, err = start(context.Background(), testContainerID) _, err = start(context.Background(), testContainerID)

View File

@ -12,6 +12,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/kata-containers/runtime/pkg/katautils"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci" "github.com/kata-containers/runtime/virtcontainers/pkg/oci"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -40,7 +41,7 @@ instance of a container.`,
} }
func state(ctx context.Context, containerID string) error { func state(ctx context.Context, containerID string) error {
span, _ := trace(ctx, "state") span, _ := katautils.Trace(ctx, "state")
defer span.Finish() defer span.Finish()
kataLog = kataLog.WithField("container", containerID) kataLog = kataLog.WithField("container", containerID)

View File

@ -13,6 +13,7 @@ import (
"strconv" "strconv"
"github.com/docker/go-units" "github.com/docker/go-units"
"github.com/kata-containers/runtime/pkg/katautils"
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -132,7 +133,7 @@ other options are ignored.
return err return err
} }
span, _ := trace(ctx, "update") span, _ := katautils.Trace(ctx, "update")
defer span.Finish() defer span.Finish()
if context.Args().Present() == false { if context.Args().Present() == false {

View File

@ -8,15 +8,13 @@ package main
import ( import (
"fmt" "fmt"
"os" "os"
"os/exec"
"strings" "strings"
"github.com/kata-containers/runtime/pkg/katautils" "github.com/kata-containers/runtime/pkg/katautils"
) )
const ( const (
unknown = "<<unknown>>" unknown = "<<unknown>>"
k8sEmptyDir = "kubernetes.io~empty-dir"
) )
// variables to allow tests to modify the values // variables to allow tests to modify the values
@ -28,34 +26,6 @@ var (
osReleaseClr = "/usr/lib/os-release" osReleaseClr = "/usr/lib/os-release"
) )
func fileExists(path string) bool {
if _, err := os.Stat(path); os.IsNotExist(err) {
return false
}
return true
}
// IsEphemeralStorage returns true if the given path
// to the storage belongs to kubernetes ephemeral storage
//
// This method depends on a specific path used by k8s
// to detect if it's of type ephemeral. As of now,
// this is a very k8s specific solution that works
// but in future there should be a better way for this
// method to determine if the path is for ephemeral
// volume type
func IsEphemeralStorage(path string) bool {
splitSourceSlice := strings.Split(path, "/")
if len(splitSourceSlice) > 1 {
storageType := splitSourceSlice[len(splitSourceSlice)-2]
if storageType == k8sEmptyDir {
return true
}
}
return false
}
func getKernelVersion() (string, error) { func getKernelVersion() (string, error) {
contents, err := katautils.GetFileContents(procVersion) contents, err := katautils.GetFileContents(procVersion)
if err != nil { if err != nil {
@ -151,27 +121,3 @@ func genericGetCPUDetails() (vendor, model string, err error) {
return vendor, model, nil return vendor, model, nil
} }
// runCommandFull returns the commands space-trimmed standard output and
// error on success. Note that if the command fails, the requested output will
// still be returned, along with an error.
func runCommandFull(args []string, includeStderr bool) (string, error) {
cmd := exec.Command(args[0], args[1:]...)
var err error
var bytes []byte
if includeStderr {
bytes, err = cmd.CombinedOutput()
} else {
bytes, err = cmd.Output()
}
trimmed := strings.TrimSpace(string(bytes))
return trimmed, err
}
// runCommand returns the commands space-trimmed standard output on success
func runCommand(args []string) (string, error) {
return runCommandFull(args, false)
}

View File

@ -12,6 +12,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/kata-containers/runtime/pkg/katautils"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -24,7 +25,7 @@ func TestFileExists(t *testing.T) {
file := filepath.Join(dir, "foo") file := filepath.Join(dir, "foo")
assert.False(t, fileExists(file), assert.False(t, katautils.FileExists(file),
fmt.Sprintf("File %q should not exist", file)) fmt.Sprintf("File %q should not exist", file))
err = createEmptyFile(file) err = createEmptyFile(file)
@ -32,24 +33,10 @@ func TestFileExists(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
assert.True(t, fileExists(file), assert.True(t, katautils.FileExists(file),
fmt.Sprintf("File %q should exist", file)) fmt.Sprintf("File %q should exist", file))
} }
func TestIsEphemeralStorage(t *testing.T) {
sampleEphePath := "/var/lib/kubelet/pods/366c3a75-4869-11e8-b479-507b9ddd5ce4/volumes/kubernetes.io~empty-dir/cache-volume"
isEphe := IsEphemeralStorage(sampleEphePath)
if !isEphe {
t.Fatalf("Unable to correctly determine volume type")
}
sampleEphePath = "/var/lib/kubelet/pods/366c3a75-4869-11e8-b479-507b9ddd5ce4/volumes/cache-volume"
isEphe = IsEphemeralStorage(sampleEphePath)
if isEphe {
t.Fatalf("Unable to correctly determine volume type")
}
}
func TestGetKernelVersion(t *testing.T) { func TestGetKernelVersion(t *testing.T) {
type testData struct { type testData struct {
contents string contents string
@ -187,13 +174,13 @@ VERSION_ID="%s"
} }
func TestUtilsRunCommand(t *testing.T) { func TestUtilsRunCommand(t *testing.T) {
output, err := runCommand([]string{"true"}) output, err := katautils.RunCommand([]string{"true"})
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "", output) assert.Equal(t, "", output)
} }
func TestUtilsRunCommandCaptureStdout(t *testing.T) { func TestUtilsRunCommandCaptureStdout(t *testing.T) {
output, err := runCommand([]string{"echo", "hello"}) output, err := katautils.RunCommand([]string{"echo", "hello"})
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "hello", output) assert.Equal(t, "hello", output)
} }
@ -201,7 +188,7 @@ func TestUtilsRunCommandCaptureStdout(t *testing.T) {
func TestUtilsRunCommandIgnoreStderr(t *testing.T) { func TestUtilsRunCommandIgnoreStderr(t *testing.T) {
args := []string{"/bin/sh", "-c", "echo foo >&2;exit 0"} args := []string{"/bin/sh", "-c", "echo foo >&2;exit 0"}
output, err := runCommand(args) output, err := katautils.RunCommand(args)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "", output) assert.Equal(t, "", output)
} }
@ -224,7 +211,7 @@ func TestUtilsRunCommandInvalidCmds(t *testing.T) {
} }
for _, args := range invalidCommands { for _, args := range invalidCommands {
output, err := runCommand(args) output, err := katautils.RunCommand(args)
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, "", output) assert.Equal(t, "", output)
} }

View File

@ -6,6 +6,7 @@
package main package main
import ( import (
"github.com/kata-containers/runtime/pkg/katautils"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -18,7 +19,7 @@ var versionCLICommand = cli.Command{
return err return err
} }
span, _ := trace(ctx, "version") span, _ := katautils.Trace(ctx, "version")
defer span.Finish() defer span.Finish()
cli.VersionPrinter(context) cli.VersionPrinter(context)

View File

@ -1,4 +1,4 @@
// Copyright (c) 2017 Intel Corporation // Copyright (c) 2018 Intel Corporation
// Copyright (c) 2018 HyperHQ Inc. // Copyright (c) 2018 HyperHQ Inc.
// //
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
@ -15,6 +15,7 @@ var defaultInitrdPath = "/usr/share/kata-containers/kata-containers-initrd.img"
var defaultFirmwarePath = "" var defaultFirmwarePath = ""
var defaultMachineAccelerators = "" var defaultMachineAccelerators = ""
var defaultShimPath = "/usr/libexec/kata-containers/kata-shim" var defaultShimPath = "/usr/libexec/kata-containers/kata-shim"
var systemdUnitName = "kata-containers.target"
const defaultKernelParams = "" const defaultKernelParams = ""
const defaultMachineType = "pc" const defaultMachineType = "pc"

View File

@ -1,4 +1,4 @@
// Copyright (c) 2017 Intel Corporation // Copyright (c) 2018 Intel Corporation
// Copyright (c) 2018 HyperHQ Inc. // Copyright (c) 2018 HyperHQ Inc.
// //
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
@ -28,6 +28,9 @@ const (
var ( var (
defaultProxy = vc.KataProxyType defaultProxy = vc.KataProxyType
defaultShim = vc.KataShimType defaultShim = vc.KataShimType
// if true, enable opentracing support.
tracing = false
) )
// The TOML configuration file contains a number of sections (or // The TOML configuration file contains a number of sections (or
@ -531,7 +534,7 @@ func updateRuntimeConfig(configPath string, tomlConf tomlConfig, config *oci.Run
return nil return nil
} }
func initConfig(builtIn bool) (config oci.RuntimeConfig, err error) { func initConfig() (config oci.RuntimeConfig, err error) {
var defaultAgentConfig interface{} var defaultAgentConfig interface{}
defaultHypervisorConfig := vc.HypervisorConfig{ defaultHypervisorConfig := vc.HypervisorConfig{
@ -565,13 +568,6 @@ func initConfig(builtIn bool) (config oci.RuntimeConfig, err error) {
defaultAgentConfig = vc.HyperConfig{} defaultAgentConfig = vc.HyperConfig{}
if builtIn {
defaultProxy = vc.KataBuiltInProxyType
defaultShim = vc.KataBuiltInShimType
defaultAgentConfig = vc.KataAgentConfig{LongLiveConn: true}
}
config = oci.RuntimeConfig{ config = oci.RuntimeConfig{
HypervisorType: defaultHypervisor, HypervisorType: defaultHypervisor,
HypervisorConfig: defaultHypervisorConfig, HypervisorConfig: defaultHypervisorConfig,
@ -592,12 +588,12 @@ func initConfig(builtIn bool) (config oci.RuntimeConfig, err error) {
// //
// All paths are resolved fully meaning if this function does not return an // All paths are resolved fully meaning if this function does not return an
// error, all paths are valid at the time of the call. // error, all paths are valid at the time of the call.
func LoadConfiguration(configPath string, ignoreLogging, builtIn bool) (resolvedConfigPath string, config oci.RuntimeConfig, tracing bool, err error) { func LoadConfiguration(configPath string, ignoreLogging, builtIn bool) (resolvedConfigPath string, config oci.RuntimeConfig, err error) {
var resolved string var resolved string
config, err = initConfig(builtIn) config, err = initConfig()
if err != nil { if err != nil {
return "", oci.RuntimeConfig{}, tracing, err return "", oci.RuntimeConfig{}, err
} }
if configPath == "" { if configPath == "" {
@ -607,18 +603,18 @@ func LoadConfiguration(configPath string, ignoreLogging, builtIn bool) (resolved
} }
if err != nil { if err != nil {
return "", config, tracing, fmt.Errorf("Cannot find usable config file (%v)", err) return "", config, fmt.Errorf("Cannot find usable config file (%v)", err)
} }
configData, err := ioutil.ReadFile(resolved) configData, err := ioutil.ReadFile(resolved)
if err != nil { if err != nil {
return "", config, tracing, err return "", config, err
} }
var tomlConf tomlConfig var tomlConf tomlConfig
_, err = toml.Decode(string(configData), &tomlConf) _, err = toml.Decode(string(configData), &tomlConf)
if err != nil { if err != nil {
return "", config, tracing, err return "", config, err
} }
config.Debug = tomlConf.Runtime.Debug config.Debug = tomlConf.Runtime.Debug
@ -633,14 +629,14 @@ func LoadConfiguration(configPath string, ignoreLogging, builtIn bool) (resolved
if tomlConf.Runtime.InterNetworkModel != "" { if tomlConf.Runtime.InterNetworkModel != "" {
err = config.InterNetworkModel.SetModel(tomlConf.Runtime.InterNetworkModel) err = config.InterNetworkModel.SetModel(tomlConf.Runtime.InterNetworkModel)
if err != nil { if err != nil {
return "", config, tracing, err return "", config, err
} }
} }
if !ignoreLogging { if !ignoreLogging {
err := handleSystemLog("", "") err := handleSystemLog("", "")
if err != nil { if err != nil {
return "", config, tracing, err return "", config, err
} }
kataUtilsLogger.WithFields( kataUtilsLogger.WithFields(
@ -650,13 +646,13 @@ func LoadConfiguration(configPath string, ignoreLogging, builtIn bool) (resolved
}).Info("loaded configuration") }).Info("loaded configuration")
} }
if err := updateRuntimeConfig(resolved, tomlConf, &config); err != nil { if err := updateConfig(resolved, tomlConf, &config, builtIn); err != nil {
return "", config, tracing, err return "", config, err
} }
config.DisableNewNetNs = tomlConf.Runtime.DisableNewNetNs config.DisableNewNetNs = tomlConf.Runtime.DisableNewNetNs
if err := checkNetNsConfig(config); err != nil { if err := checkNetNsConfig(config); err != nil {
return "", config, tracing, err return "", config, err
} }
// use no proxy if HypervisorConfig.UseVSock is true // use no proxy if HypervisorConfig.UseVSock is true
@ -667,10 +663,26 @@ func LoadConfiguration(configPath string, ignoreLogging, builtIn bool) (resolved
} }
if err := checkHypervisorConfig(config.HypervisorConfig); err != nil { if err := checkHypervisorConfig(config.HypervisorConfig); err != nil {
return "", config, tracing, err return "", config, err
} }
return resolved, config, tracing, nil return resolved, config, nil
}
func updateConfig(configPath string, tomlConf tomlConfig, config *oci.RuntimeConfig, builtIn bool) error {
if err := updateRuntimeConfig(configPath, tomlConf, config); err != nil {
return err
}
if builtIn {
config.ProxyType = vc.KataBuiltInProxyType
config.ShimType = vc.KataBuiltInShimType
config.AgentType = vc.KataContainersAgent
config.AgentConfig = vc.KataAgentConfig{LongLiveConn: true}
}
return nil
} }
// checkNetNsConfig performs sanity checks on disable_new_netns config. // checkNetNsConfig performs sanity checks on disable_new_netns config.

View File

@ -1,4 +1,4 @@
// Copyright (c) 2017 Intel Corporation // Copyright (c) 2018 Intel Corporation
// Copyright (c) 2018 HyperHQ Inc. // Copyright (c) 2018 HyperHQ Inc.
// //
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
@ -249,7 +249,7 @@ func testLoadConfiguration(t *testing.T, dir string,
assert.NoError(t, err) assert.NoError(t, err)
} }
resolvedConfigPath, config, _, err := LoadConfiguration(file, ignoreLogging, false) resolvedConfigPath, config, err := LoadConfiguration(file, ignoreLogging, false)
if expectFail { if expectFail {
assert.Error(t, err) assert.Error(t, err)
@ -558,7 +558,7 @@ func TestMinimalRuntimeConfig(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
_, config, _, err := LoadConfiguration(configPath, false, false) _, config, err := LoadConfiguration(configPath, false, false)
if err == nil { if err == nil {
t.Fatalf("Expected loadConfiguration to fail as shim path does not exist: %+v", config) t.Fatalf("Expected loadConfiguration to fail as shim path does not exist: %+v", config)
} }
@ -583,7 +583,7 @@ func TestMinimalRuntimeConfig(t *testing.T) {
t.Error(err) t.Error(err)
} }
_, config, _, err = LoadConfiguration(configPath, false, false) _, config, err = LoadConfiguration(configPath, false, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -712,7 +712,7 @@ func TestMinimalRuntimeConfigWithVsock(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
_, config, _, err := LoadConfiguration(configPath, false, false) _, config, err := LoadConfiguration(configPath, false, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

248
pkg/katautils/create.go Normal file
View File

@ -0,0 +1,248 @@
// Copyright (c) 2018 Intel Corporation
// Copyright (c) 2018 HyperHQ Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
package katautils
import (
"context"
"fmt"
"strings"
vc "github.com/kata-containers/runtime/virtcontainers"
vf "github.com/kata-containers/runtime/virtcontainers/factory"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci"
)
// GetKernelParamsFunc use a variable to allow tests to modify its value
var GetKernelParamsFunc = getKernelParams
var systemdKernelParam = []vc.Param{
{
Key: "init",
Value: "/usr/lib/systemd/systemd",
},
{
Key: "systemd.unit",
Value: systemdUnitName,
},
{
Key: "systemd.mask",
Value: "systemd-networkd.service",
},
{
Key: "systemd.mask",
Value: "systemd-networkd.socket",
},
}
func getKernelParams(needSystemd bool) []vc.Param {
p := []vc.Param{}
if needSystemd {
p = append(p, systemdKernelParam...)
}
return p
}
func needSystemd(config vc.HypervisorConfig) bool {
return config.ImagePath != ""
}
// HandleFactory set the factory
func HandleFactory(ctx context.Context, vci vc.VC, runtimeConfig *oci.RuntimeConfig) {
if !runtimeConfig.FactoryConfig.Template {
return
}
factoryConfig := vf.Config{
Template: true,
VMConfig: vc.VMConfig{
HypervisorType: runtimeConfig.HypervisorType,
HypervisorConfig: runtimeConfig.HypervisorConfig,
AgentType: runtimeConfig.AgentType,
AgentConfig: runtimeConfig.AgentConfig,
},
}
kataUtilsLogger.WithField("factory", factoryConfig).Info("load vm factory")
f, err := vf.NewFactory(ctx, factoryConfig, true)
if err != nil {
kataUtilsLogger.WithError(err).Warn("load vm factory failed, about to create new one")
f, err = vf.NewFactory(ctx, factoryConfig, false)
if err != nil {
kataUtilsLogger.WithError(err).Warn("create vm factory failed")
return
}
}
vci.SetFactory(ctx, f)
}
// SetKernelParams adds the user-specified kernel parameters (from the
// configuration file) to the defaults so that the former take priority.
func SetKernelParams(containerID string, runtimeConfig *oci.RuntimeConfig) error {
defaultKernelParams := GetKernelParamsFunc(needSystemd(runtimeConfig.HypervisorConfig))
if runtimeConfig.HypervisorConfig.Debug {
strParams := vc.SerializeParams(defaultKernelParams, "=")
formatted := strings.Join(strParams, " ")
kataUtilsLogger.WithField("default-kernel-parameters", formatted).Debug()
}
// retrieve the parameters specified in the config file
userKernelParams := runtimeConfig.HypervisorConfig.KernelParams
// reset
runtimeConfig.HypervisorConfig.KernelParams = []vc.Param{}
// first, add default values
for _, p := range defaultKernelParams {
if err := (runtimeConfig).AddKernelParam(p); err != nil {
return err
}
}
// now re-add the user-specified values so that they take priority.
for _, p := range userKernelParams {
if err := (runtimeConfig).AddKernelParam(p); err != nil {
return err
}
}
return nil
}
// SetEphemeralStorageType sets the mount type to 'ephemeral'
// if the mount source path is provisioned by k8s for ephemeral storage.
// For the given pod ephemeral volume is created only once
// backed by tmpfs inside the VM. For successive containers
// of the same pod the already existing volume is reused.
func SetEphemeralStorageType(ociSpec oci.CompatOCISpec) oci.CompatOCISpec {
for idx, mnt := range ociSpec.Mounts {
if IsEphemeralStorage(mnt.Source) {
ociSpec.Mounts[idx].Type = "ephemeral"
}
}
return ociSpec
}
// CreateSandbox create a sandbox container
func CreateSandbox(ctx context.Context, vci vc.VC, ociSpec oci.CompatOCISpec, runtimeConfig oci.RuntimeConfig,
containerID, bundlePath, console string, disableOutput, systemdCgroup, builtIn bool) (vc.VCSandbox, vc.Process, error) {
span, ctx := Trace(ctx, "createSandbox")
defer span.Finish()
err := SetKernelParams(containerID, &runtimeConfig)
if err != nil {
return nil, vc.Process{}, err
}
sandboxConfig, err := oci.SandboxConfig(ociSpec, runtimeConfig, bundlePath, containerID, console, disableOutput, systemdCgroup)
if err != nil {
return nil, vc.Process{}, err
}
if builtIn {
sandboxConfig.Stateful = true
}
// Important to create the network namespace before the sandbox is
// created, because it is not responsible for the creation of the
// netns if it does not exist.
if err := SetupNetworkNamespace(&sandboxConfig.NetworkConfig); err != nil {
return nil, vc.Process{}, err
}
// Run pre-start OCI hooks.
err = EnterNetNS(sandboxConfig.NetworkConfig.NetNSPath, func() error {
return PreStartHooks(ctx, ociSpec, containerID, bundlePath)
})
if err != nil {
return nil, vc.Process{}, err
}
sandbox, err := vci.CreateSandbox(ctx, sandboxConfig)
if err != nil {
return nil, vc.Process{}, err
}
sid := sandbox.ID()
kataUtilsLogger = kataUtilsLogger.WithField("sandbox", sid)
span.SetTag("sandbox", sid)
containers := sandbox.GetAllContainers()
if len(containers) != 1 {
return nil, vc.Process{}, fmt.Errorf("BUG: Container list from sandbox is wrong, expecting only one container, found %d containers", len(containers))
}
if !builtIn {
err = AddContainerIDMapping(ctx, containerID, sandbox.ID())
if err != nil {
return nil, vc.Process{}, err
}
}
return sandbox, containers[0].Process(), nil
}
// CreateContainer create a container
func CreateContainer(ctx context.Context, vci vc.VC, sandbox vc.VCSandbox, ociSpec oci.CompatOCISpec, containerID, bundlePath, console string, disableOutput, builtIn bool) (vc.Process, error) {
var c vc.VCContainer
span, ctx := Trace(ctx, "createContainer")
defer span.Finish()
ociSpec = SetEphemeralStorageType(ociSpec)
contConfig, err := oci.ContainerConfig(ociSpec, bundlePath, containerID, console, disableOutput)
if err != nil {
return vc.Process{}, err
}
sandboxID, err := ociSpec.SandboxID()
if err != nil {
return vc.Process{}, err
}
span.SetTag("sandbox", sandboxID)
if builtIn {
c, err = sandbox.CreateContainer(contConfig)
if err != nil {
return vc.Process{}, err
}
} else {
kataUtilsLogger = kataUtilsLogger.WithField("sandbox", sandboxID)
sandbox, c, err = vci.CreateContainer(ctx, sandboxID, contConfig)
if err != nil {
return vc.Process{}, err
}
if err := AddContainerIDMapping(ctx, containerID, sandboxID); err != nil {
return vc.Process{}, err
}
kataUtilsLogger = kataUtilsLogger.WithField("sandbox", sandboxID)
if err := AddContainerIDMapping(ctx, containerID, sandboxID); err != nil {
return vc.Process{}, err
}
}
// Run pre-start OCI hooks.
err = EnterNetNS(sandbox.GetNetNs(), func() error {
return PreStartHooks(ctx, ociSpec, containerID, bundlePath)
})
if err != nil {
return vc.Process{}, err
}
return c.Process(), nil
}

View File

@ -0,0 +1,455 @@
// Copyright (c) 2018 Intel Corporation
// Copyright (c) 2018 HyperHQ Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
package katautils
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"testing"
vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci"
"github.com/kata-containers/runtime/virtcontainers/pkg/vcmock"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
)
const (
testConsole = "/dev/pts/999"
testContainerTypeAnnotation = "io.kubernetes.cri-o.ContainerType"
testSandboxIDAnnotation = "io.kubernetes.cri-o.SandboxID"
testContainerTypeContainer = "container"
)
var (
testBundleDir = ""
// testingImpl is a concrete mock RVC implementation used for testing
testingImpl = &vcmock.VCMock{}
)
// readOCIConfig returns an OCI spec.
func readOCIConfigFile(configPath string) (oci.CompatOCISpec, error) {
if configPath == "" {
return oci.CompatOCISpec{}, errors.New("BUG: need config file path")
}
data, err := ioutil.ReadFile(configPath)
if err != nil {
return oci.CompatOCISpec{}, err
}
var ociSpec oci.CompatOCISpec
if err := json.Unmarshal(data, &ociSpec); err != nil {
return oci.CompatOCISpec{}, err
}
caps, err := oci.ContainerCapabilities(ociSpec)
if err != nil {
return oci.CompatOCISpec{}, err
}
ociSpec.Process.Capabilities = caps
return ociSpec, nil
}
func writeOCIConfigFile(spec oci.CompatOCISpec, configPath string) error {
if configPath == "" {
return errors.New("BUG: need config file path")
}
bytes, err := json.MarshalIndent(spec, "", "\t")
if err != nil {
return err
}
return ioutil.WriteFile(configPath, bytes, testFileMode)
}
// Create an OCI bundle in the specified directory.
//
// Note that the directory will be created, but it's parent is expected to exist.
//
// This function works by copying the already-created test bundle. Ideally,
// the bundle would be recreated for each test, but createRootfs() uses
// docker which on some systems is too slow, resulting in the tests timing
// out.
func makeOCIBundle(bundleDir string) error {
from := testBundleDir
to := bundleDir
// only the basename of bundleDir needs to exist as bundleDir
// will get created by cp(1).
base := filepath.Dir(bundleDir)
for _, dir := range []string{from, base} {
if !FileExists(dir) {
return fmt.Errorf("BUG: directory %v should exist", dir)
}
}
output, err := RunCommandFull([]string{"cp", "-a", from, to}, true)
if err != nil {
return fmt.Errorf("failed to copy test OCI bundle from %v to %v: %v (output: %v)", from, to, err, output)
}
return nil
}
// newTestRuntimeConfig creates a new RuntimeConfig
func newTestRuntimeConfig(dir, consolePath string, create bool) (oci.RuntimeConfig, error) {
if dir == "" {
return oci.RuntimeConfig{}, errors.New("BUG: need directory")
}
hypervisorConfig, err := newTestHypervisorConfig(dir, create)
if err != nil {
return oci.RuntimeConfig{}, err
}
return oci.RuntimeConfig{
HypervisorType: vc.QemuHypervisor,
HypervisorConfig: hypervisorConfig,
AgentType: vc.KataContainersAgent,
ProxyType: vc.CCProxyType,
ShimType: vc.CCShimType,
Console: consolePath,
}, nil
}
// newTestHypervisorConfig creaets a new virtcontainers
// HypervisorConfig, ensuring that the required resources are also
// created.
//
// Note: no parameter validation in case caller wishes to create an invalid
// object.
func newTestHypervisorConfig(dir string, create bool) (vc.HypervisorConfig, error) {
kernelPath := path.Join(dir, "kernel")
imagePath := path.Join(dir, "image")
hypervisorPath := path.Join(dir, "hypervisor")
if create {
for _, file := range []string{kernelPath, imagePath, hypervisorPath} {
err := createEmptyFile(file)
if err != nil {
return vc.HypervisorConfig{}, err
}
}
}
return vc.HypervisorConfig{
KernelPath: kernelPath,
ImagePath: imagePath,
HypervisorPath: hypervisorPath,
HypervisorMachineType: "pc-lite",
}, nil
}
// return the value of the *last* param with the specified key
func findLastParam(key string, params []vc.Param) (string, error) {
if key == "" {
return "", errors.New("ERROR: need non-nil key")
}
l := len(params)
if l == 0 {
return "", errors.New("ERROR: no params")
}
for i := l - 1; i >= 0; i-- {
p := params[i]
if key == p.Key {
return p.Value, nil
}
}
return "", fmt.Errorf("no param called %q found", name)
}
func TestSetEphemeralStorageType(t *testing.T) {
assert := assert.New(t)
ociSpec := oci.CompatOCISpec{}
var ociMounts []specs.Mount
mount := specs.Mount{
Source: "/var/lib/kubelet/pods/366c3a77-4869-11e8-b479-507b9ddd5ce4/volumes/kubernetes.io~empty-dir/cache-volume",
}
ociMounts = append(ociMounts, mount)
ociSpec.Mounts = ociMounts
ociSpec = SetEphemeralStorageType(ociSpec)
mountType := ociSpec.Mounts[0].Type
assert.Equal(mountType, "ephemeral",
"Unexpected mount type, got %s expected ephemeral", mountType)
}
func TestSetKernelParams(t *testing.T) {
assert := assert.New(t)
config := oci.RuntimeConfig{}
assert.Empty(config.HypervisorConfig.KernelParams)
err := SetKernelParams(testContainerID, &config)
assert.NoError(err)
if needSystemd(config.HypervisorConfig) {
assert.NotEmpty(config.HypervisorConfig.KernelParams)
}
}
func TestSetKernelParamsUserOptionTakesPriority(t *testing.T) {
assert := assert.New(t)
initName := "init"
initValue := "/sbin/myinit"
ipName := "ip"
ipValue := "127.0.0.1"
params := []vc.Param{
{Key: initName, Value: initValue},
{Key: ipName, Value: ipValue},
}
hypervisorConfig := vc.HypervisorConfig{
KernelParams: params,
}
// Config containing user-specified kernel parameters
config := oci.RuntimeConfig{
HypervisorConfig: hypervisorConfig,
}
assert.NotEmpty(config.HypervisorConfig.KernelParams)
err := SetKernelParams(testContainerID, &config)
assert.NoError(err)
kernelParams := config.HypervisorConfig.KernelParams
init, err := findLastParam(initName, kernelParams)
assert.NoError(err)
assert.Equal(initValue, init)
ip, err := findLastParam(ipName, kernelParams)
assert.NoError(err)
assert.Equal(ipValue, ip)
}
func TestCreateSandboxConfigFail(t *testing.T) {
assert := assert.New(t)
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(FileExists(ociConfigFile))
spec, err := readOCIConfigFile(ociConfigFile)
assert.NoError(err)
quota := int64(0)
limit := int64(0)
spec.Linux.Resources.Memory = &specs.LinuxMemory{
Limit: &limit,
}
spec.Linux.Resources.CPU = &specs.LinuxCPU{
// specify an invalid value
Quota: &quota,
}
_, _, err = CreateSandbox(context.Background(), testingImpl, spec, runtimeConfig, testContainerID, bundlePath, testConsole, true, true, false)
assert.Error(err)
}
func TestCreateSandboxFail(t *testing.T) {
if os.Geteuid() != 0 {
t.Skip(testDisabledNeedNonRoot)
}
assert := assert.New(t)
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(FileExists(ociConfigFile))
spec, err := readOCIConfigFile(ociConfigFile)
assert.NoError(err)
_, _, err = CreateSandbox(context.Background(), testingImpl, spec, runtimeConfig, testContainerID, bundlePath, testConsole, true, true, false)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
}
func TestCreateContainerContainerConfigFail(t *testing.T) {
assert := assert.New(t)
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(FileExists(ociConfigFile))
spec, err := readOCIConfigFile(ociConfigFile)
assert.NoError(err)
// Set invalid container type
containerType := "你好,世界"
spec.Annotations = make(map[string]string)
spec.Annotations[testContainerTypeAnnotation] = containerType
// rewrite file
err = writeOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
for _, disableOutput := range []bool{true, false} {
_, err = CreateContainer(context.Background(), testingImpl, nil, spec, testContainerID, bundlePath, testConsole, disableOutput, false)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
assert.True(strings.Contains(err.Error(), containerType))
os.RemoveAll(path)
}
}
func TestCreateContainerFail(t *testing.T) {
assert := assert.New(t)
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(FileExists(ociConfigFile))
spec, err := readOCIConfigFile(ociConfigFile)
assert.NoError(err)
// set expected container type and sandboxID
spec.Annotations = make(map[string]string)
spec.Annotations[testContainerTypeAnnotation] = testContainerTypeContainer
spec.Annotations[testSandboxIDAnnotation] = testSandboxID
// rewrite file
err = writeOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
for _, disableOutput := range []bool{true, false} {
_, err = CreateContainer(context.Background(), testingImpl, nil, spec, testContainerID, bundlePath, testConsole, disableOutput, false)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
os.RemoveAll(path)
}
}
func TestCreateContainer(t *testing.T) {
assert := assert.New(t)
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
testingImpl.CreateContainerFunc = func(ctx context.Context, sandboxID string, containerConfig vc.ContainerConfig) (vc.VCSandbox, vc.VCContainer, error) {
return &vcmock.Sandbox{}, &vcmock.Container{}, nil
}
defer func() {
testingImpl.CreateContainerFunc = nil
}()
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(FileExists(ociConfigFile))
spec, err := readOCIConfigFile(ociConfigFile)
assert.NoError(err)
// set expected container type and sandboxID
spec.Annotations = make(map[string]string)
spec.Annotations[testContainerTypeAnnotation] = testContainerTypeContainer
spec.Annotations[testSandboxIDAnnotation] = testSandboxID
// rewrite file
err = writeOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
for _, disableOutput := range []bool{true, false} {
_, err = CreateContainer(context.Background(), testingImpl, nil, spec, testContainerID, bundlePath, testConsole, disableOutput, false)
assert.NoError(err)
os.RemoveAll(path)
}
}

View File

@ -1,9 +1,10 @@
// Copyright (c) 2018 Intel Corporation // Copyright (c) 2018 Intel Corporation
// Copyright (c) 2018 HyperHQ Inc.
// //
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// //
package main package katautils
import ( import (
"bytes" "bytes"
@ -24,11 +25,11 @@ import (
// Logger returns a logrus logger appropriate for logging hook messages // Logger returns a logrus logger appropriate for logging hook messages
func hookLogger() *logrus.Entry { func hookLogger() *logrus.Entry {
return kataLog.WithField("subsystem", "hook") return kataUtilsLogger.WithField("subsystem", "hook")
} }
func runHook(ctx context.Context, hook specs.Hook, cid, bundlePath string) error { func runHook(ctx context.Context, hook specs.Hook, cid, bundlePath string) error {
span, _ := trace(ctx, "hook") span, _ := Trace(ctx, "hook")
defer span.Finish() defer span.Finish()
span.SetTag("subsystem", "runHook") span.SetTag("subsystem", "runHook")
@ -91,7 +92,7 @@ func runHook(ctx context.Context, hook specs.Hook, cid, bundlePath string) error
} }
func runHooks(ctx context.Context, hooks []specs.Hook, cid, bundlePath, hookType string) error { func runHooks(ctx context.Context, hooks []specs.Hook, cid, bundlePath, hookType string) error {
span, _ := trace(ctx, "hooks") span, _ := Trace(ctx, "hooks")
defer span.Finish() defer span.Finish()
span.SetTag("subsystem", hookType) span.SetTag("subsystem", hookType)
@ -110,7 +111,8 @@ func runHooks(ctx context.Context, hooks []specs.Hook, cid, bundlePath, hookType
return nil return nil
} }
func preStartHooks(ctx context.Context, spec oci.CompatOCISpec, cid, bundlePath string) error { // PreStartHooks run the hooks before start container
func PreStartHooks(ctx context.Context, spec oci.CompatOCISpec, cid, bundlePath string) error {
// If no hook available, nothing needs to be done. // If no hook available, nothing needs to be done.
if spec.Hooks == nil { if spec.Hooks == nil {
return nil return nil
@ -119,7 +121,8 @@ func preStartHooks(ctx context.Context, spec oci.CompatOCISpec, cid, bundlePath
return runHooks(ctx, spec.Hooks.Prestart, cid, bundlePath, "pre-start") return runHooks(ctx, spec.Hooks.Prestart, cid, bundlePath, "pre-start")
} }
func postStartHooks(ctx context.Context, spec oci.CompatOCISpec, cid, bundlePath string) error { // PostStartHooks run the hooks just after start container
func PostStartHooks(ctx context.Context, spec oci.CompatOCISpec, cid, bundlePath string) error {
// If no hook available, nothing needs to be done. // If no hook available, nothing needs to be done.
if spec.Hooks == nil { if spec.Hooks == nil {
return nil return nil
@ -128,7 +131,8 @@ func postStartHooks(ctx context.Context, spec oci.CompatOCISpec, cid, bundlePath
return runHooks(ctx, spec.Hooks.Poststart, cid, bundlePath, "post-start") return runHooks(ctx, spec.Hooks.Poststart, cid, bundlePath, "post-start")
} }
func postStopHooks(ctx context.Context, spec oci.CompatOCISpec, cid, bundlePath string) error { // PostStopHooks run the hooks after stop container
func PostStopHooks(ctx context.Context, spec oci.CompatOCISpec, cid, bundlePath string) error {
// If no hook available, nothing needs to be done. // If no hook available, nothing needs to be done.
if spec.Hooks == nil { if spec.Hooks == nil {
return nil return nil

View File

@ -1,9 +1,10 @@
// Copyright (c) 2018 Intel Corporation // Copyright (c) 2018 Intel Corporation
// Copyright (c) 2018 HyperHQ Inc.
// //
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// //
package main package katautils
import ( import (
"context" "context"
@ -95,7 +96,7 @@ func TestPreStartHooks(t *testing.T) {
// Hooks field is nil // Hooks field is nil
spec := oci.CompatOCISpec{} spec := oci.CompatOCISpec{}
err := preStartHooks(ctx, spec, "", "") err := PreStartHooks(ctx, spec, "", "")
assert.NoError(err) assert.NoError(err)
// Hooks list is empty // Hooks list is empty
@ -104,7 +105,7 @@ func TestPreStartHooks(t *testing.T) {
Hooks: &specs.Hooks{}, Hooks: &specs.Hooks{},
}, },
} }
err = preStartHooks(ctx, spec, "", "") err = PreStartHooks(ctx, spec, "", "")
assert.NoError(err) assert.NoError(err)
// Run with timeout 0 // Run with timeout 0
@ -116,7 +117,7 @@ func TestPreStartHooks(t *testing.T) {
}, },
}, },
} }
err = preStartHooks(ctx, spec, testSandboxID, testBundlePath) err = PreStartHooks(ctx, spec, testSandboxID, testBundlePath)
assert.NoError(err) assert.NoError(err)
// Failure due to wrong hook // Failure due to wrong hook
@ -128,7 +129,7 @@ func TestPreStartHooks(t *testing.T) {
}, },
}, },
} }
err = preStartHooks(ctx, spec, testSandboxID, testBundlePath) err = PreStartHooks(ctx, spec, testSandboxID, testBundlePath)
assert.Error(err) assert.Error(err)
} }
@ -143,7 +144,7 @@ func TestPostStartHooks(t *testing.T) {
// Hooks field is nil // Hooks field is nil
spec := oci.CompatOCISpec{} spec := oci.CompatOCISpec{}
err := postStartHooks(ctx, spec, "", "") err := PostStartHooks(ctx, spec, "", "")
assert.NoError(err) assert.NoError(err)
// Hooks list is empty // Hooks list is empty
@ -152,7 +153,7 @@ func TestPostStartHooks(t *testing.T) {
Hooks: &specs.Hooks{}, Hooks: &specs.Hooks{},
}, },
} }
err = postStartHooks(ctx, spec, "", "") err = PostStartHooks(ctx, spec, "", "")
assert.NoError(err) assert.NoError(err)
// Run with timeout 0 // Run with timeout 0
@ -164,7 +165,7 @@ func TestPostStartHooks(t *testing.T) {
}, },
}, },
} }
err = postStartHooks(ctx, spec, testSandboxID, testBundlePath) err = PostStartHooks(ctx, spec, testSandboxID, testBundlePath)
assert.NoError(err) assert.NoError(err)
// Failure due to wrong hook // Failure due to wrong hook
@ -176,7 +177,7 @@ func TestPostStartHooks(t *testing.T) {
}, },
}, },
} }
err = postStartHooks(ctx, spec, testSandboxID, testBundlePath) err = PostStartHooks(ctx, spec, testSandboxID, testBundlePath)
assert.Error(err) assert.Error(err)
} }
@ -191,7 +192,7 @@ func TestPostStopHooks(t *testing.T) {
// Hooks field is nil // Hooks field is nil
spec := oci.CompatOCISpec{} spec := oci.CompatOCISpec{}
err := postStopHooks(ctx, spec, "", "") err := PostStopHooks(ctx, spec, "", "")
assert.NoError(err) assert.NoError(err)
// Hooks list is empty // Hooks list is empty
@ -200,7 +201,7 @@ func TestPostStopHooks(t *testing.T) {
Hooks: &specs.Hooks{}, Hooks: &specs.Hooks{},
}, },
} }
err = postStopHooks(ctx, spec, "", "") err = PostStopHooks(ctx, spec, "", "")
assert.NoError(err) assert.NoError(err)
// Run with timeout 0 // Run with timeout 0
@ -212,7 +213,7 @@ func TestPostStopHooks(t *testing.T) {
}, },
}, },
} }
err = postStopHooks(ctx, spec, testSandboxID, testBundlePath) err = PostStopHooks(ctx, spec, testSandboxID, testBundlePath)
assert.NoError(err) assert.NoError(err)
// Failure due to wrong hook // Failure due to wrong hook
@ -224,6 +225,6 @@ func TestPostStopHooks(t *testing.T) {
}, },
}, },
} }
err = postStopHooks(ctx, spec, testSandboxID, testBundlePath) err = PostStopHooks(ctx, spec, testSandboxID, testBundlePath)
assert.Error(err) assert.Error(err)
} }

View File

@ -1,4 +1,4 @@
// Copyright (c) 2017 Intel Corporation // Copyright (c) 2018 Intel Corporation
// Copyright (c) 2018 HyperHQ Inc. // Copyright (c) 2018 HyperHQ Inc.
// //
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0

View File

@ -1,4 +1,4 @@
// Copyright (c) 2017 Intel Corporation // Copyright (c) 2018 Intel Corporation
// Copyright (c) 2018 HyperHQ Inc. // Copyright (c) 2018 HyperHQ Inc.
// //
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0

181
pkg/katautils/network.go Normal file
View File

@ -0,0 +1,181 @@
// Copyright (c) 2018 Intel Corporation
// Copyright (c) 2018 HyperHQ Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
package katautils
import (
"bufio"
"fmt"
"os"
"path/filepath"
goruntime "runtime"
"strings"
"github.com/containernetworking/plugins/pkg/ns"
vc "github.com/kata-containers/runtime/virtcontainers"
"golang.org/x/sys/unix"
)
const procMountInfoFile = "/proc/self/mountinfo"
// EnterNetNS is free from any call to a go routine, and it calls
// into runtime.LockOSThread(), meaning it won't be executed in a
// different thread than the one expected by the caller.
func EnterNetNS(netNSPath string, cb func() error) error {
if netNSPath == "" {
return cb()
}
goruntime.LockOSThread()
defer goruntime.UnlockOSThread()
currentNS, err := ns.GetCurrentNS()
if err != nil {
return err
}
defer currentNS.Close()
targetNS, err := ns.GetNS(netNSPath)
if err != nil {
return err
}
if err := targetNS.Set(); err != nil {
return err
}
defer currentNS.Set()
return cb()
}
// SetupNetworkNamespace create a network namespace
func SetupNetworkNamespace(config *vc.NetworkConfig) error {
if config.DisableNewNetNs {
kataUtilsLogger.Info("DisableNewNetNs is on, shim and hypervisor are running in the host netns")
return nil
}
if config.NetNSPath == "" {
n, err := ns.NewNS()
if err != nil {
return err
}
config.NetNSPath = n.Path()
config.NetNsCreated = true
return nil
}
isHostNs, err := hostNetworkingRequested(config.NetNSPath)
if err != nil {
return err
}
if isHostNs {
return fmt.Errorf("Host networking requested, not supported by runtime")
}
return nil
}
// getNetNsFromBindMount returns the network namespace for the bind-mounted path
func getNetNsFromBindMount(nsPath string, procMountFile string) (string, error) {
netNsMountType := "nsfs"
// Resolve all symlinks in the path as the mountinfo file contains
// resolved paths.
nsPath, err := filepath.EvalSymlinks(nsPath)
if err != nil {
return "", err
}
f, err := os.Open(procMountFile)
if err != nil {
return "", err
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
text := scanner.Text()
// Scan the mountinfo file to search for the network namespace path
// This file contains mounts in the eg format:
// "711 26 0:3 net:[4026532009] /run/docker/netns/default rw shared:535 - nsfs nsfs rw"
//
// Reference: https://www.kernel.org/doc/Documentation/filesystems/proc.txt
// We are interested in the first 9 fields of this file,
// to check for the correct mount type.
fields := strings.Split(text, " ")
if len(fields) < 9 {
continue
}
// We check here if the mount type is a network namespace mount type, namely "nsfs"
mountTypeFieldIdx := 8
if fields[mountTypeFieldIdx] != netNsMountType {
continue
}
// This is the mount point/destination for the mount
mntDestIdx := 4
if fields[mntDestIdx] != nsPath {
continue
}
// This is the root/source of the mount
return fields[3], nil
}
return "", nil
}
// hostNetworkingRequested checks if the network namespace requested is the
// same as the current process.
func hostNetworkingRequested(configNetNs string) (bool, error) {
var evalNS, nsPath, currentNsPath string
var err error
// Net namespace provided as "/proc/pid/ns/net" or "/proc/<pid>/task/<tid>/ns/net"
if strings.HasPrefix(configNetNs, "/proc") && strings.HasSuffix(configNetNs, "/ns/net") {
if _, err := os.Stat(configNetNs); err != nil {
return false, err
}
// 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.
if _, err = filepath.EvalSymlinks(configNetNs); err != nil {
nsPath = err.(*os.PathError).Path
} else {
return false, fmt.Errorf("Net namespace path %s is not a symlink", configNetNs)
}
_, evalNS = filepath.Split(nsPath)
} else {
// Bind-mounted path provided
evalNS, _ = getNetNsFromBindMount(configNetNs, procMountInfoFile)
}
currentNS := fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid())
if _, err = filepath.EvalSymlinks(currentNS); err != nil {
currentNsPath = err.(*os.PathError).Path
} else {
return false, fmt.Errorf("Unexpected: Current network namespace path is not a symlink")
}
_, evalCurrentNS := filepath.Split(currentNsPath)
if evalNS == evalCurrentNS {
return true, nil
}
return false, nil
}

View File

@ -0,0 +1,150 @@
// Copyright (c) 2018 Huawei Corporation.
//
// SPDX-License-Identifier: Apache-2.0
//
package katautils
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"syscall"
"testing"
"github.com/containernetworking/plugins/pkg/ns"
vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/stretchr/testify/assert"
"golang.org/x/sys/unix"
)
func TestGetNetNsFromBindMount(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
mountFile := filepath.Join(tmpdir, "mountInfo")
nsPath := filepath.Join(tmpdir, "ns123")
// Non-existent namespace path
_, err = getNetNsFromBindMount(nsPath, mountFile)
assert.NotNil(err)
tmpNSPath := filepath.Join(tmpdir, "testNetNs")
f, err := os.Create(tmpNSPath)
assert.NoError(err)
defer f.Close()
type testData struct {
contents string
expectedResult string
}
data := []testData{
{fmt.Sprintf("711 26 0:3 net:[4026532008] %s rw shared:535 - nsfs nsfs rw", tmpNSPath), "net:[4026532008]"},
{"711 26 0:3 net:[4026532008] /run/netns/ns123 rw shared:535 - tmpfs tmpfs rw", ""},
{"a a a a a a a - b c d", ""},
{"", ""},
}
for i, d := range data {
err := ioutil.WriteFile(mountFile, []byte(d.contents), 0640)
assert.NoError(err)
path, err := getNetNsFromBindMount(tmpNSPath, mountFile)
assert.NoError(err, fmt.Sprintf("got %q, test data: %+v", path, d))
assert.Equal(d.expectedResult, path, "Test %d, expected %s, got %s", i, d.expectedResult, path)
}
}
func TestHostNetworkingRequested(t *testing.T) {
assert := assert.New(t)
if os.Geteuid() != 0 {
t.Skip(testDisabledNeedRoot)
}
// Network namespace same as the host
selfNsPath := "/proc/self/ns/net"
isHostNs, err := hostNetworkingRequested(selfNsPath)
assert.NoError(err)
assert.True(isHostNs)
// Non-existent netns path
nsPath := "/proc/123456789/ns/net"
_, err = hostNetworkingRequested(nsPath)
assert.Error(err)
// Bind-mounted Netns
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
// Create a bind mount to the current network namespace.
tmpFile := filepath.Join(tmpdir, "testNetNs")
f, err := os.Create(tmpFile)
assert.NoError(err)
defer f.Close()
err = syscall.Mount(selfNsPath, tmpFile, "bind", syscall.MS_BIND, "")
assert.Nil(err)
isHostNs, err = hostNetworkingRequested(tmpFile)
assert.NoError(err)
assert.True(isHostNs)
syscall.Unmount(tmpFile, 0)
}
func TestSetupNetworkNamespace(t *testing.T) {
if os.Geteuid() != 0 {
t.Skip(testDisabledNeedNonRoot)
}
assert := assert.New(t)
// Network namespace same as the host
config := &vc.NetworkConfig{
NetNSPath: "/proc/self/ns/net",
}
err := SetupNetworkNamespace(config)
assert.Error(err)
// Non-existent netns path
config = &vc.NetworkConfig{
NetNSPath: "/proc/123456789/ns/net",
}
err = SetupNetworkNamespace(config)
assert.Error(err)
// Existent netns path
n, err := ns.NewNS()
assert.NoError(err)
config = &vc.NetworkConfig{
NetNSPath: n.Path(),
}
err = SetupNetworkNamespace(config)
assert.NoError(err)
n.Close()
// Empty netns path
config = &vc.NetworkConfig{}
err = SetupNetworkNamespace(config)
assert.NoError(err)
n, err = ns.GetNS(config.NetNSPath)
assert.NoError(err)
assert.NotNil(n)
assert.True(config.NetNsCreated)
n.Close()
unix.Unmount(config.NetNSPath, unix.MNT_DETACH)
os.RemoveAll(config.NetNSPath)
// Config with DisableNewNetNs
config = &vc.NetworkConfig{DisableNewNetNs: true}
err = SetupNetworkNamespace(config)
assert.NoError(err)
}

92
pkg/katautils/oci.go Normal file
View File

@ -0,0 +1,92 @@
// Copyright (c) 2018 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package katautils
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
const ctrsMappingDirMode = os.FileMode(0750)
var ctrsMapTreePath = "/var/run/kata-containers/containers-mapping"
// SetCtrsMapTreePath let the testcases change the ctrsMapTreePath to a test dir
func SetCtrsMapTreePath(path string) {
ctrsMapTreePath = path
}
// FetchContainerIDMapping This function assumes it should find only one file inside the container
// ID directory. If there are several files, we could not determine which
// file name corresponds to the sandbox ID associated, and this would throw
// an error.
func FetchContainerIDMapping(containerID string) (string, error) {
if containerID == "" {
return "", fmt.Errorf("Missing container ID")
}
dirPath := filepath.Join(ctrsMapTreePath, containerID)
files, err := ioutil.ReadDir(dirPath)
if err != nil {
if os.IsNotExist(err) {
return "", nil
}
return "", err
}
if len(files) != 1 {
return "", fmt.Errorf("Too many files (%d) in %q", len(files), dirPath)
}
return files[0].Name(), nil
}
// AddContainerIDMapping add a container id mapping to sandbox id
func AddContainerIDMapping(ctx context.Context, containerID, sandboxID string) error {
span, _ := Trace(ctx, "addContainerIDMapping")
defer span.Finish()
if containerID == "" {
return fmt.Errorf("Missing container ID")
}
if sandboxID == "" {
return fmt.Errorf("Missing sandbox ID")
}
parentPath := filepath.Join(ctrsMapTreePath, containerID)
if err := os.RemoveAll(parentPath); err != nil {
return err
}
path := filepath.Join(parentPath, sandboxID)
if err := os.MkdirAll(path, ctrsMappingDirMode); err != nil {
return err
}
return nil
}
// DelContainerIDMapping delete container id mapping from a sandbox
func DelContainerIDMapping(ctx context.Context, containerID string) error {
span, _ := Trace(ctx, "delContainerIDMapping")
defer span.Finish()
if containerID == "" {
return fmt.Errorf("Missing container ID")
}
path := filepath.Join(ctrsMapTreePath, containerID)
return os.RemoveAll(path)
}

135
pkg/katautils/oci_test.go Normal file
View File

@ -0,0 +1,135 @@
// Copyright (c) 2018 Intel Corporation
// Copyright (c) 2018 HyperHQ Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
package katautils
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
)
func createTempContainerIDMapping(containerID, sandboxID string) (string, error) {
tmpDir, err := ioutil.TempDir("", "containers-mapping")
if err != nil {
return "", err
}
ctrsMapTreePath = tmpDir
path := filepath.Join(ctrsMapTreePath, containerID, sandboxID)
if err := os.MkdirAll(path, 0750); err != nil {
return "", err
}
return tmpDir, nil
}
func TestFetchContainerIDMappingContainerIDEmptyFailure(t *testing.T) {
assert := assert.New(t)
sandboxID, err := FetchContainerIDMapping("")
assert.Error(err)
assert.Empty(sandboxID)
}
func TestFetchContainerIDMappingEmptyMappingSuccess(t *testing.T) {
assert := assert.New(t)
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
sandboxID, err := FetchContainerIDMapping(testContainerID)
assert.NoError(err)
assert.Empty(sandboxID)
}
func TestFetchContainerIDMappingTooManyFilesFailure(t *testing.T) {
assert := assert.New(t)
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
err = os.MkdirAll(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID+"2"), ctrsMappingDirMode)
assert.NoError(err)
sandboxID, err := FetchContainerIDMapping(testContainerID)
assert.Error(err)
assert.Empty(sandboxID)
}
func TestFetchContainerIDMappingSuccess(t *testing.T) {
assert := assert.New(t)
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
sandboxID, err := FetchContainerIDMapping(testContainerID)
assert.NoError(err)
assert.Equal(sandboxID, testSandboxID)
}
func TestAddContainerIDMappingContainerIDEmptyFailure(t *testing.T) {
assert := assert.New(t)
err := AddContainerIDMapping(context.Background(), "", testSandboxID)
assert.Error(err)
}
func TestAddContainerIDMappingSandboxIDEmptyFailure(t *testing.T) {
assert := assert.New(t)
err := AddContainerIDMapping(context.Background(), testContainerID, "")
assert.Error(err)
}
func TestAddContainerIDMappingSuccess(t *testing.T) {
assert := assert.New(t)
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
_, err = os.Stat(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID))
assert.True(os.IsNotExist(err))
err = AddContainerIDMapping(context.Background(), testContainerID, testSandboxID)
assert.NoError(err)
_, err = os.Stat(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID))
assert.NoError(err)
}
func TestDelContainerIDMappingContainerIDEmptyFailure(t *testing.T) {
assert := assert.New(t)
err := DelContainerIDMapping(context.Background(), "")
assert.Error(err)
}
func TestDelContainerIDMappingSuccess(t *testing.T) {
assert := assert.New(t)
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
_, err = os.Stat(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID))
assert.NoError(err)
err = DelContainerIDMapping(context.Background(), testContainerID)
assert.NoError(err)
_, err = os.Stat(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID))
assert.True(os.IsNotExist(err))
}

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// //
package main package katautils
import ( import (
"context" "context"
@ -22,14 +22,15 @@ type traceLogger struct {
var tracerCloser io.Closer var tracerCloser io.Closer
func (t traceLogger) Error(msg string) { func (t traceLogger) Error(msg string) {
kataLog.Error(msg) kataUtilsLogger.Error(msg)
} }
func (t traceLogger) Infof(msg string, args ...interface{}) { func (t traceLogger) Infof(msg string, args ...interface{}) {
kataLog.Infof(msg, args...) kataUtilsLogger.Infof(msg, args...)
} }
func createTracer(name string) (opentracing.Tracer, error) { // CreateTracer create a tracer
func CreateTracer(name string) (opentracing.Tracer, error) {
cfg := &config.Configuration{ cfg := &config.Configuration{
ServiceName: name, ServiceName: name,
@ -61,8 +62,8 @@ func createTracer(name string) (opentracing.Tracer, error) {
return tracer, nil return tracer, nil
} }
// stopTracing() ends all tracing, reporting the spans to the collector. // StopTracing ends all tracing, reporting the spans to the collector.
func stopTracing(ctx context.Context) { func StopTracing(ctx context.Context) {
if !tracing { if !tracing {
return return
} }
@ -74,12 +75,14 @@ func stopTracing(ctx context.Context) {
} }
// report all possible spans to the collector // report all possible spans to the collector
tracerCloser.Close() if tracerCloser != nil {
tracerCloser.Close()
}
} }
// trace creates a new tracing span based on the specified name and parent // Trace creates a new tracing span based on the specified name and parent
// context. // context.
func trace(parent context.Context, name string) (opentracing.Span, context.Context) { func Trace(parent context.Context, name string) (opentracing.Span, context.Context) {
span, ctx := opentracing.StartSpanFromContext(parent, name) span, ctx := opentracing.StartSpanFromContext(parent, name)
span.SetTag("source", "runtime") span.SetTag("source", "runtime")

View File

@ -1,4 +1,4 @@
// Copyright (c) 2017 Intel Corporation // Copyright (c) 2018 Intel Corporation
// Copyright (c) 2018 HyperHQ Inc. // Copyright (c) 2018 HyperHQ Inc.
// //
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
@ -10,10 +10,45 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"strings"
"syscall" "syscall"
) )
const (
k8sEmptyDir = "kubernetes.io~empty-dir"
)
// FileExists test is a file exiting or not
func FileExists(path string) bool {
if _, err := os.Stat(path); os.IsNotExist(err) {
return false
}
return true
}
// IsEphemeralStorage returns true if the given path
// to the storage belongs to kubernetes ephemeral storage
//
// This method depends on a specific path used by k8s
// to detect if it's of type ephemeral. As of now,
// this is a very k8s specific solution that works
// but in future there should be a better way for this
// method to determine if the path is for ephemeral
// volume type
func IsEphemeralStorage(path string) bool {
splitSourceSlice := strings.Split(path, "/")
if len(splitSourceSlice) > 1 {
storageType := splitSourceSlice[len(splitSourceSlice)-2]
if storageType == k8sEmptyDir {
return true
}
}
return false
}
// ResolvePath returns the fully resolved and expanded value of the // ResolvePath returns the fully resolved and expanded value of the
// specified path. // specified path.
func ResolvePath(path string) (string, error) { func ResolvePath(path string) (string, error) {
@ -75,3 +110,27 @@ func GetFileContents(file string) (string, error) {
return string(bytes), nil return string(bytes), nil
} }
// RunCommandFull returns the commands space-trimmed standard output and
// error on success. Note that if the command fails, the requested output will
// still be returned, along with an error.
func RunCommandFull(args []string, includeStderr bool) (string, error) {
cmd := exec.Command(args[0], args[1:]...)
var err error
var bytes []byte
if includeStderr {
bytes, err = cmd.CombinedOutput()
} else {
bytes, err = cmd.Output()
}
trimmed := strings.TrimSpace(string(bytes))
return trimmed, err
}
// RunCommand returns the commands space-trimmed standard output on success
func RunCommand(args []string) (string, error) {
return RunCommandFull(args, false)
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2017 Intel Corporation // Copyright (c) 2018 Intel Corporation
// Copyright (c) 2018 HyperHQ Inc. // Copyright (c) 2018 HyperHQ Inc.
// //
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
@ -7,14 +7,18 @@
package katautils package katautils
import ( import (
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec"
"path" "path"
"path/filepath" "path/filepath"
"strings"
"syscall" "syscall"
"testing" "testing"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -22,7 +26,16 @@ const (
testDirMode = os.FileMode(0750) testDirMode = os.FileMode(0750)
testFileMode = os.FileMode(0640) testFileMode = os.FileMode(0640)
testDisabledNeedRoot = "Test disabled as requires root user"
testDisabledNeedNonRoot = "Test disabled as requires non-root user" testDisabledNeedNonRoot = "Test disabled as requires non-root user"
// small docker image used to create root filesystems from
testDockerImage = "busybox"
testSandboxID = "99999999-9999-9999-99999999999999999"
testContainerID = "1"
testBundle = "bundle"
specConfig = "config.json"
) )
var testDir = "" var testDir = ""
@ -37,6 +50,148 @@ func init() {
} }
fmt.Printf("INFO: test directory is %v\n", testDir) fmt.Printf("INFO: test directory is %v\n", testDir)
testBundleDir = filepath.Join(testDir, testBundle)
err = os.MkdirAll(testBundleDir, testDirMode)
if err != nil {
panic(fmt.Sprintf("ERROR: failed to create bundle directory %v: %v", testBundleDir, err))
}
fmt.Printf("INFO: creating OCI bundle in %v for tests to use\n", testBundleDir)
err = realMakeOCIBundle(testBundleDir)
if err != nil {
panic(fmt.Sprintf("ERROR: failed to create OCI bundle: %v", err))
}
}
// createOCIConfig creates an OCI configuration (spec) file in
// the bundle directory specified (which must exist).
func createOCIConfig(bundleDir string) error {
if bundleDir == "" {
return errors.New("BUG: Need bundle directory")
}
if !FileExists(bundleDir) {
return fmt.Errorf("BUG: Bundle directory %s does not exist", bundleDir)
}
var configCmd string
// Search for a suitable version of runc to use to generate
// the OCI config file.
for _, cmd := range []string{"docker-runc", "runc"} {
fullPath, err := exec.LookPath(cmd)
if err == nil {
configCmd = fullPath
break
}
}
if configCmd == "" {
return errors.New("Cannot find command to generate OCI config file")
}
_, err := RunCommand([]string{configCmd, "spec", "--bundle", bundleDir})
if err != nil {
return err
}
specFile := filepath.Join(bundleDir, specConfig)
if !FileExists(specFile) {
return fmt.Errorf("generated OCI config file does not exist: %v", specFile)
}
return nil
}
// realMakeOCIBundle will create an OCI bundle (including the "config.json"
// config file) in the directory specified (which must already exist).
//
// XXX: Note that tests should *NOT* call this function - they should
// XXX: instead call makeOCIBundle().
func realMakeOCIBundle(bundleDir string) error {
if bundleDir == "" {
return errors.New("BUG: Need bundle directory")
}
if !FileExists(bundleDir) {
return fmt.Errorf("BUG: Bundle directory %v does not exist", bundleDir)
}
err := createOCIConfig(bundleDir)
if err != nil {
return err
}
// Note the unusual parameter (a directory, not the config
// file to parse!)
spec, err := oci.ParseConfigJSON(bundleDir)
if err != nil {
return err
}
// Determine the rootfs directory name the OCI config refers to
ociRootPath := spec.Root.Path
rootfsDir := filepath.Join(bundleDir, ociRootPath)
if strings.HasPrefix(ociRootPath, "/") {
return fmt.Errorf("Cannot handle absolute rootfs as bundle must be unique to each test")
}
err = createRootfs(rootfsDir)
if err != nil {
return err
}
return nil
}
// createRootfs creates a minimal root filesystem below the specified
// directory.
func createRootfs(dir string) error {
err := os.MkdirAll(dir, testDirMode)
if err != nil {
return err
}
container, err := RunCommand([]string{"docker", "create", testDockerImage})
if err != nil {
return err
}
cmd1 := exec.Command("docker", "export", container)
cmd2 := exec.Command("tar", "-C", dir, "-xvf", "-")
cmd1Stdout, err := cmd1.StdoutPipe()
if err != nil {
return err
}
cmd2.Stdin = cmd1Stdout
err = cmd2.Start()
if err != nil {
return err
}
err = cmd1.Run()
if err != nil {
return err
}
err = cmd2.Wait()
if err != nil {
return err
}
// Clean up
_, err = RunCommand([]string{"docker", "rm", container})
if err != nil {
return err
}
return nil
} }
func createFile(file, contents string) error { func createFile(file, contents string) error {
@ -211,3 +366,17 @@ func TestGetFileContents(t *testing.T) {
assert.Equal(t, contents, d.contents) assert.Equal(t, contents, d.contents)
} }
} }
func TestIsEphemeralStorage(t *testing.T) {
sampleEphePath := "/var/lib/kubelet/pods/366c3a75-4869-11e8-b479-507b9ddd5ce4/volumes/kubernetes.io~empty-dir/cache-volume"
isEphe := IsEphemeralStorage(sampleEphePath)
if !isEphe {
t.Fatalf("Unable to correctly determine volume type")
}
sampleEphePath = "/var/lib/kubelet/pods/366c3a75-4869-11e8-b479-507b9ddd5ce4/volumes/cache-volume"
isEphe = IsEphemeralStorage(sampleEphePath)
if isEphe {
t.Fatalf("Unable to correctly determine volume type")
}
}