runtime: delete unused sub-commands.

This PR delete codes not used anymore.

Fixes: #332

Signed-off-by: bin liu <bin@hyper.sh>
This commit is contained in:
bin liu 2020-06-01 03:45:18 +00:00
parent e3a3818f7a
commit 069505e2d5
46 changed files with 66 additions and 10453 deletions

View File

@ -1,185 +0,0 @@
// Copyright (c) 2014,2015,2016 Docker, Inc.
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"errors"
"fmt"
"os"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/compatoci"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/oci"
"github.com/urfave/cli"
)
var createCLICommand = cli.Command{
Name: "create",
Usage: "Create a container",
ArgsUsage: `<container-id>
<container-id> is your name for the instance of the container that you
are starting. The name you provide for the container instance must be unique
on your host.`,
Description: `The create command creates an instance of a container for a bundle. The
bundle is a directory with a specification file named "` + specConfig + `" and a
root filesystem.
The specification file includes an args parameter. The args parameter is
used to specify command(s) that get run when the container is started.
To change the command(s) that get executed on start, edit the args
parameter of the spec.`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "bundle, b",
Value: "",
Usage: `path to the root of the bundle directory, defaults to the current directory`,
},
cli.StringFlag{
Name: "console",
Value: "",
Usage: "path to a pseudo terminal",
},
cli.StringFlag{
Name: "console-socket",
Value: "",
Usage: "path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal",
},
cli.StringFlag{
Name: "pid-file",
Value: "",
Usage: "specify the file to write the process id to",
},
cli.BoolFlag{
Name: "no-pivot",
Usage: "warning: this flag is meaningless to kata-runtime, just defined in order to be compatible with docker in ramdisk",
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
runtimeConfig, ok := context.App.Metadata["runtimeConfig"].(oci.RuntimeConfig)
if !ok {
return errors.New("invalid runtime config")
}
console, err := setupConsole(context.String("console"), context.String("console-socket"))
if err != nil {
return err
}
return create(ctx, context.Args().First(),
context.String("bundle"),
console,
context.String("pid-file"),
true,
context.Bool("systemd-cgroup"),
runtimeConfig,
)
},
}
func create(ctx context.Context, containerID, bundlePath, console, pidFilePath string, detach, systemdCgroup bool,
runtimeConfig oci.RuntimeConfig) error {
var err error
span, ctx := katautils.Trace(ctx, "create")
defer span.Finish()
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(ctx, kataLog)
span.SetTag("container", containerID)
if bundlePath == "" {
cwd, err := os.Getwd()
if err != nil {
return err
}
kataLog.WithField("directory", cwd).Debug("Defaulting bundle path to current directory")
bundlePath = cwd
}
// Checks the MUST and MUST NOT from OCI runtime specification
if bundlePath, err = validCreateParams(ctx, containerID, bundlePath); err != nil {
return err
}
ociSpec, err := compatoci.ParseConfigJSON(bundlePath)
if err != nil {
return err
}
containerType, err := oci.ContainerType(ociSpec)
if err != nil {
return err
}
katautils.HandleFactory(ctx, vci, &runtimeConfig)
disableOutput := noNeedForOutput(detach, ociSpec.Process.Terminal)
//rootfs has been mounted by containerd shim
rootFs := vc.RootFs{Mounted: true}
var process vc.Process
switch containerType {
case vc.PodSandbox:
_, process, err = katautils.CreateSandbox(ctx, vci, ociSpec, runtimeConfig, rootFs, containerID, bundlePath, console, disableOutput, systemdCgroup, false)
if err != nil {
return err
}
case vc.PodContainer:
process, err = katautils.CreateContainer(ctx, vci, nil, ociSpec, rootFs, containerID, bundlePath, console, disableOutput, false)
if err != nil {
return err
}
}
// Creation of PID file has to be the last thing done in the create
// because containerd considers the create complete after this file
// is created.
return createPIDFile(ctx, pidFilePath, process.Pid)
}
func createPIDFile(ctx context.Context, pidFilePath string, pid int) error {
span, _ := katautils.Trace(ctx, "createPIDFile")
defer span.Finish()
if pidFilePath == "" {
// runtime should not fail since pid file is optional
return nil
}
if err := os.RemoveAll(pidFilePath); err != nil {
return err
}
f, err := os.Create(pidFilePath)
if err != nil {
return err
}
defer f.Close()
pidStr := fmt.Sprintf("%d", pid)
n, err := f.WriteString(pidStr)
if err != nil {
return err
}
if n < len(pidStr) {
return fmt.Errorf("Could not write pid to '%s': only %d bytes written out of %d", pidFilePath, n, len(pidStr))
}
return nil
}

View File

@ -1,724 +0,0 @@
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"testing"
ktu "github.com/kata-containers/kata-containers/src/runtime/pkg/katatestutils"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/compatoci"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/oci"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/vcmock"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli"
)
const (
testPID = 100
testConsole = "/dev/pts/999"
testContainerTypeAnnotation = "io.kubernetes.cri-o.ContainerType"
testContainerTypeSandbox = "sandbox"
testContainerTypeContainer = "container"
)
var (
testStrPID = fmt.Sprintf("%d", testPID)
ctrsMapTreePath = "/var/run/kata-containers/containers-mapping"
)
func TestCreatePIDFileSuccessful(t *testing.T) {
pidDirPath, err := ioutil.TempDir(testDir, "pid-path-")
if err != nil {
t.Fatalf("Could not create temporary PID directory: %s", err)
}
pidFilePath := filepath.Join(pidDirPath, "pid-file-path")
if err := createPIDFile(context.Background(), pidFilePath, testPID); err != nil {
t.Fatal(err)
}
fileBytes, err := ioutil.ReadFile(pidFilePath)
if err != nil {
os.RemoveAll(pidFilePath)
t.Fatalf("Could not read %q: %s", pidFilePath, err)
}
if string(fileBytes) != testStrPID {
os.RemoveAll(pidFilePath)
t.Fatalf("PID %s read from %q different from expected PID %s", string(fileBytes), pidFilePath, testStrPID)
}
os.RemoveAll(pidFilePath)
}
func TestCreatePIDFileEmptyPathSuccessful(t *testing.T) {
file := ""
if err := createPIDFile(context.Background(), file, testPID); err != nil {
t.Fatalf("This test should not fail (pidFilePath %q, pid %d)", file, testPID)
}
}
func TestCreatePIDFileUnableToRemove(t *testing.T) {
if tc.NotValid(ktu.NeedNonRoot()) {
// The os.FileMode(0000) trick doesn't work for root.
t.Skip(ktu.TestDisabledNeedNonRoot)
}
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
subdir := filepath.Join(tmpdir, "dir")
file := filepath.Join(subdir, "pidfile")
// stop non-root user from removing the directory later
err = os.MkdirAll(subdir, os.FileMode(0000))
assert.NoError(err)
err = createPIDFile(context.Background(), file, testPID)
assert.Error(err)
// let it be deleted
os.Chmod(subdir, testDirMode)
}
func TestCreatePIDFileUnableToCreate(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
subdir := filepath.Join(tmpdir, "dir")
file := filepath.Join(subdir, "pidfile")
err = createPIDFile(context.Background(), file, testPID)
// subdir doesn't exist
assert.Error(err)
os.Chmod(subdir, testDirMode)
}
func TestCreateCLIFunctionNoRuntimeConfig(t *testing.T) {
assert := assert.New(t)
ctx := createCLIContext(nil)
ctx.App.Name = "foo"
ctx.App.Metadata["foo"] = "bar"
fn, ok := createCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
err := fn(ctx)
// no runtime config in the Metadata
assert.Error(err)
}
func TestCreateCLIFunctionSetupConsoleFail(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
subdir := filepath.Join(tmpdir, "dir")
// does not exist
consoleSocketPath := filepath.Join(subdir, "console")
set := flag.NewFlagSet("", 0)
set.String("console-socket", consoleSocketPath, "")
ctx := createCLIContext(set)
ctx.App.Name = "foo"
ctx.App.Metadata["runtimeConfig"] = runtimeConfig
fn, ok := createCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
err = fn(ctx)
// failed to setup console
assert.Error(err)
}
func TestCreateCLIFunctionCreateFail(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
set := flag.NewFlagSet("", 0)
set.String("console-socket", "", "")
ctx := createCLIContext(set)
ctx.App.Name = "foo"
ctx.App.Metadata["runtimeConfig"] = runtimeConfig
fn, ok := createCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
err = fn(ctx)
// create() failed
assert.Error(err)
}
func TestCreateInvalidArgs(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
MockContainers: []*vcmock.Container{
{MockID: testContainerID},
{MockID: testContainerID},
{MockID: testContainerID},
},
}
testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) {
return sandbox, nil
}
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
defer func() {
testingImpl.CreateSandboxFunc = nil
}()
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)
pidFilePath := filepath.Join(tmpdir, "pidfile.txt")
type testData struct {
containerID string
bundlePath string
console string
pidFilePath string
detach bool
systemdCgroup bool
runtimeConfig oci.RuntimeConfig
}
data := []testData{
{"", "", "", "", false, false, oci.RuntimeConfig{}},
{"", "", "", "", true, true, oci.RuntimeConfig{}},
{"foo", "", "", "", true, false, oci.RuntimeConfig{}},
{testContainerID, bundlePath, testConsole, pidFilePath, false, false, runtimeConfig},
{testContainerID, bundlePath, testConsole, pidFilePath, true, true, runtimeConfig},
}
for i, d := range data {
err := create(context.Background(), d.containerID, d.bundlePath, d.console, d.pidFilePath, d.detach, d.systemdCgroup, d.runtimeConfig)
assert.Errorf(err, "test %d (%+v)", i, d)
}
}
func TestCreateInvalidConfigJSON(t *testing.T) {
assert := assert.New(t)
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
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)
pidFilePath := filepath.Join(tmpdir, "pidfile.txt")
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(katautils.FileExists(ociConfigFile))
f, err := os.OpenFile(ociConfigFile, os.O_APPEND|os.O_WRONLY, testFileMode)
assert.NoError(err)
// invalidate the JSON
_, err = f.WriteString("{")
assert.NoError(err)
f.Close()
for detach := range []bool{true, false} {
err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, true, runtimeConfig)
assert.Errorf(err, "%+v", detach)
assert.False(vcmock.IsMockError(err))
os.RemoveAll(path)
}
}
func TestCreateInvalidContainerType(t *testing.T) {
assert := assert.New(t)
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
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)
pidFilePath := filepath.Join(tmpdir, "pidfile.txt")
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(katautils.FileExists(ociConfigFile))
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
// Force an invalid container type
spec.Annotations = make(map[string]string)
spec.Annotations[testContainerTypeAnnotation] = "I-am-not-a-valid-container-type"
// rewrite the file
err = writeOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
for detach := range []bool{true, false} {
err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, true, runtimeConfig)
assert.Errorf(err, "%+v", detach)
assert.False(vcmock.IsMockError(err))
os.RemoveAll(path)
}
}
func TestCreateContainerInvalid(t *testing.T) {
assert := assert.New(t)
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
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)
pidFilePath := filepath.Join(tmpdir, "pidfile.txt")
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(katautils.FileExists(ociConfigFile))
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
// Force createContainer() to be called.
spec.Annotations = make(map[string]string)
spec.Annotations[testContainerTypeAnnotation] = testContainerTypeContainer
// rewrite the file
err = writeOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
for detach := range []bool{true, false} {
err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, true, runtimeConfig)
assert.Errorf(err, "%+v", detach)
assert.False(vcmock.IsMockError(err))
os.RemoveAll(path)
}
}
func TestCreateProcessCgroupsPathSuccessful(t *testing.T) {
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(ktu.TestDisabledNeedRoot)
}
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
MockContainers: []*vcmock.Container{
{MockID: testContainerID},
},
}
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) {
return sandbox, nil
}
defer func() {
testingImpl.CreateSandboxFunc = nil
}()
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)
pidFilePath := filepath.Join(tmpdir, "pidfile.txt")
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(katautils.FileExists(ociConfigFile))
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
// Force sandbox-type container
spec.Annotations = make(map[string]string)
spec.Annotations[testContainerTypeAnnotation] = testContainerTypeSandbox
// Set a limit to ensure processCgroupsPath() considers the
// cgroup part of the spec
limit := int64(1024 * 1024)
spec.Linux.Resources.Memory = &specs.LinuxMemory{
Limit: &limit,
}
// Set an absolute path
spec.Linux.CgroupsPath = "/this/is/a/cgroup/path"
var mounts []specs.Mount
foundMount := false
// Replace the standard cgroup destination with a temporary one.
for _, mount := range spec.Mounts {
if mount.Type == "cgroup" {
foundMount = true
cgroupDir, err := ioutil.TempDir("", "cgroup")
assert.NoError(err)
defer os.RemoveAll(cgroupDir)
mount.Destination = cgroupDir
}
mounts = append(mounts, mount)
}
assert.True(foundMount)
// Replace mounts with the newly created one.
spec.Mounts = mounts
// Rewrite the file
err = writeOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
err = create(context.Background(), testContainerID, "", testConsole, pidFilePath, false, true, runtimeConfig)
assert.Error(err, "bundle path not set")
re := regexp.MustCompile("config.json.*no such file or directory")
matches := re.FindAllStringSubmatch(err.Error(), -1)
assert.NotEmpty(matches)
for _, detach := range []bool{true, false} {
err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, detach, true, runtimeConfig)
assert.NoError(err, "detached: %+v", detach)
os.RemoveAll(path)
}
}
func TestCreateCreateCgroupsFilesFail(t *testing.T) {
if tc.NotValid(ktu.NeedNonRoot()) {
// The os.FileMode(0000) trick doesn't work for root.
t.Skip(ktu.TestDisabledNeedNonRoot)
}
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
MockContainers: []*vcmock.Container{
{MockID: testContainerID},
},
}
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) {
return sandbox, nil
}
defer func() {
testingImpl.CreateSandboxFunc = nil
}()
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)
pidFilePath := filepath.Join(tmpdir, "pidfile.txt")
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(katautils.FileExists(ociConfigFile))
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
// Force sandbox-type container
spec.Annotations = make(map[string]string)
spec.Annotations[testContainerTypeAnnotation] = testContainerTypeSandbox
// Set a limit to ensure processCgroupsPath() considers the
// cgroup part of the spec
limit := int64(1024 * 1024)
spec.Linux.Resources.Memory = &specs.LinuxMemory{
Limit: &limit,
}
// Override
cgroupsDirPath = filepath.Join(tmpdir, "cgroups")
err = os.MkdirAll(cgroupsDirPath, testDirMode)
assert.NoError(err)
// Set a relative path
spec.Linux.CgroupsPath = "./a/relative/path"
dir := filepath.Join(cgroupsDirPath, "memory")
// Stop directory from being created
err = os.MkdirAll(dir, os.FileMode(0000))
assert.NoError(err)
// Rewrite the file
err = writeOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
for detach := range []bool{true, false} {
err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, true, runtimeConfig)
assert.Errorf(err, "%+v", detach)
assert.False(vcmock.IsMockError(err))
os.RemoveAll(path)
}
}
func TestCreateCreateCreatePidFileFail(t *testing.T) {
if tc.NotValid(ktu.NeedNonRoot()) {
// The os.FileMode(0000) trick doesn't work for root.
t.Skip(ktu.TestDisabledNeedNonRoot)
}
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
MockContainers: []*vcmock.Container{
{MockID: testContainerID},
},
}
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) {
return sandbox, nil
}
defer func() {
testingImpl.CreateSandboxFunc = nil
}()
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)
pidDir := filepath.Join(tmpdir, "pid")
pidFilePath := filepath.Join(pidDir, "pidfile.txt")
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(katautils.FileExists(ociConfigFile))
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
// Force sandbox-type container
spec.Annotations = make(map[string]string)
spec.Annotations[testContainerTypeAnnotation] = testContainerTypeSandbox
// Set a limit to ensure processCgroupsPath() considers the
// cgroup part of the spec
limit := int64(1024 * 1024)
spec.Linux.Resources.Memory = &specs.LinuxMemory{
Limit: &limit,
}
// Rewrite the file
err = writeOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
// stop the pidfile from being created
err = os.MkdirAll(pidDir, os.FileMode(0000))
assert.NoError(err)
for detach := range []bool{true, false} {
err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, true, runtimeConfig)
assert.Errorf(err, "%+v", detach)
assert.False(vcmock.IsMockError(err))
os.RemoveAll(path)
}
}
func TestCreate(t *testing.T) {
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(ktu.TestDisabledNeedRoot)
}
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
MockContainers: []*vcmock.Container{
{MockID: testContainerID},
},
}
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) {
return sandbox, nil
}
defer func() {
testingImpl.CreateSandboxFunc = nil
}()
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)
pidFilePath := filepath.Join(tmpdir, "pidfile.txt")
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(katautils.FileExists(ociConfigFile))
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
// Force sandbox-type container
spec.Annotations = make(map[string]string)
spec.Annotations[testContainerTypeAnnotation] = testContainerTypeSandbox
// Set a limit to ensure processCgroupsPath() considers the
// cgroup part of the spec
limit := int64(1024 * 1024)
spec.Linux.Resources.Memory = &specs.LinuxMemory{
Limit: &limit,
}
// Rewrite the file
err = writeOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
for detach := range []bool{true, false} {
err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, true, runtimeConfig)
assert.NoError(err, "%+v", detach)
os.RemoveAll(path)
}
}

View File

@ -1,189 +0,0 @@
// Copyright (c) 2014,2015,2016 Docker, Inc.
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"fmt"
"os"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
vcAnnot "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/oci"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var deleteCLICommand = cli.Command{
Name: "delete",
Usage: "Delete any resources held by one or more containers",
ArgsUsage: `<container-id> [container-id...]
<container-id> is the name for the instance of the container.
EXAMPLE:
If the container id is "ubuntu01" and ` + name + ` list currently shows the
status of "ubuntu01" as "stopped" the following will delete resources held
for "ubuntu01" removing "ubuntu01" from the ` + name + ` list of containers:
# ` + name + ` delete ubuntu01`,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "force, f",
Usage: "Forcibly deletes the container if it is still running (uses SIGKILL)",
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
args := context.Args()
if !args.Present() {
return fmt.Errorf("Missing container ID, should at least provide one")
}
force := context.Bool("force")
for _, cID := range []string(args) {
if err := delete(ctx, cID, force); err != nil {
return err
}
}
return nil
},
}
func delete(ctx context.Context, containerID string, force bool) error {
span, ctx := katautils.Trace(ctx, "delete")
defer span.Finish()
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(ctx, kataLog)
span.SetTag("container", containerID)
// Checks the MUST and MUST NOT from OCI runtime specification
status, sandboxID, err := getExistingContainerInfo(ctx, containerID)
if err != nil {
if force {
kataLog.Warnf("Failed to get container, force will not fail: %s", err)
return nil
}
return err
}
containerID = status.ID
kataLog = kataLog.WithFields(logrus.Fields{
"container": containerID,
"sandbox": sandboxID,
})
setExternalLoggers(ctx, kataLog)
span.SetTag("container", containerID)
span.SetTag("sandbox", sandboxID)
containerType, err := oci.GetContainerType(status.Annotations)
if err != nil {
return err
}
// Retrieve OCI spec configuration.
ociSpec, err := oci.GetOCIConfig(status)
if err != nil {
return err
}
forceStop := false
if oci.StateToOCIState(status.State.State) == oci.StateRunning {
if !force {
return fmt.Errorf("Container still running, should be stopped")
}
forceStop = true
}
switch containerType {
case vc.PodSandbox:
if err := deleteSandbox(ctx, sandboxID, force); err != nil {
return err
}
case vc.PodContainer:
if err := deleteContainer(ctx, sandboxID, containerID, forceStop); err != nil {
return err
}
default:
return fmt.Errorf("Invalid container type found")
}
// Run post-stop OCI hooks.
if err := katautils.PostStopHooks(ctx, ociSpec, sandboxID, status.Annotations[vcAnnot.BundlePathKey]); err != nil {
return err
}
return katautils.DelContainerIDMapping(ctx, containerID)
}
func deleteSandbox(ctx context.Context, sandboxID string, force bool) error {
span, _ := katautils.Trace(ctx, "deleteSandbox")
defer span.Finish()
status, err := vci.StatusSandbox(ctx, sandboxID)
if err != nil {
return err
}
if oci.StateToOCIState(status.State.State) != oci.StateStopped {
if _, err := vci.StopSandbox(ctx, sandboxID, force); err != nil {
return err
}
}
if _, err := vci.DeleteSandbox(ctx, sandboxID); err != nil {
return err
}
return nil
}
func deleteContainer(ctx context.Context, sandboxID, containerID string, forceStop bool) error {
span, _ := katautils.Trace(ctx, "deleteContainer")
defer span.Finish()
if forceStop {
if _, err := vci.StopContainer(ctx, sandboxID, containerID); err != nil {
return err
}
}
if _, err := vci.DeleteContainer(ctx, sandboxID, containerID); err != nil {
return err
}
return nil
}
func removeCgroupsPath(ctx context.Context, containerID string, cgroupsPathList []string) error {
span, _ := katautils.Trace(ctx, "removeCgroupsPath")
defer span.Finish()
if len(cgroupsPathList) == 0 {
kataLog.WithField("container", containerID).Info("Cgroups files not removed because cgroupsPath was empty")
return nil
}
for _, cgroupsPath := range cgroupsPathList {
if err := os.RemoveAll(cgroupsPath); err != nil {
return err
}
}
return nil
}

View File

@ -1,626 +0,0 @@
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"flag"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli"
ktu "github.com/kata-containers/kata-containers/src/runtime/pkg/katatestutils"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
vcAnnotations "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/compatoci"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/vcmock"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
)
func testRemoveCgroupsPathSuccessful(t *testing.T, cgroupsPathList []string) {
if err := removeCgroupsPath(context.Background(), "foo", cgroupsPathList); err != nil {
t.Fatalf("This test should succeed (cgroupsPathList = %v): %s", cgroupsPathList, err)
}
}
func TestRemoveCgroupsPathEmptyPathSuccessful(t *testing.T) {
testRemoveCgroupsPathSuccessful(t, []string{})
}
func TestRemoveCgroupsPathNonEmptyPathSuccessful(t *testing.T) {
cgroupsPath, err := ioutil.TempDir(testDir, "cgroups-path-")
if err != nil {
t.Fatalf("Could not create temporary cgroups directory: %s", err)
}
if err := os.MkdirAll(cgroupsPath, testDirMode); err != nil {
t.Fatalf("CgroupsPath directory %q could not be created: %s", cgroupsPath, err)
}
testRemoveCgroupsPathSuccessful(t, []string{cgroupsPath})
if _, err := os.Stat(cgroupsPath); err == nil {
t.Fatalf("CgroupsPath directory %q should have been removed: %s", cgroupsPath, err)
}
}
func TestDeleteInvalidContainer(t *testing.T) {
assert := assert.New(t)
// Missing container id
err := delete(context.Background(), "", false)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
// Container missing in ListSandbox
err = delete(context.Background(), testContainerID, false)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
// Force to delete missing container
err = delete(context.Background(), "non-existing-test", true)
assert.NoError(err)
}
func TestDeleteMissingContainerTypeAnnotation(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID())
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: sandbox.ID(),
Annotations: map[string]string{},
}, nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
err = delete(context.Background(), sandbox.ID(), false)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
func TestDeleteInvalidConfig(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID())
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
},
}, nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
err = delete(context.Background(), sandbox.ID(), false)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
func testConfigSetup(t *testing.T) (rootPath string, bundlePath string) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
bundlePath = filepath.Join(tmpdir, "bundle")
err = os.MkdirAll(bundlePath, testDirMode)
assert.NoError(err)
err = createOCIConfig(bundlePath)
assert.NoError(err)
return tmpdir, bundlePath
}
func TestDeleteSandbox(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
rootPath, bundlePath := testConfigSetup(t)
defer os.RemoveAll(rootPath)
ociSpec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID())
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
},
State: types.ContainerState{
State: "ready",
},
Spec: &ociSpec,
}, nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
err = delete(context.Background(), sandbox.ID(), false)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
testingImpl.StatusSandboxFunc = func(ctx context.Context, sandboxID string) (vc.SandboxStatus, error) {
return vc.SandboxStatus{
ID: sandbox.ID(),
State: types.SandboxState{
State: types.StateReady,
},
}, nil
}
defer func() {
testingImpl.StatusSandboxFunc = nil
}()
err = delete(context.Background(), sandbox.ID(), false)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
testingImpl.StopSandboxFunc = func(ctx context.Context, sandboxID string, force bool) (vc.VCSandbox, error) {
return sandbox, nil
}
defer func() {
testingImpl.StopSandboxFunc = nil
}()
err = delete(context.Background(), sandbox.ID(), false)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
testingImpl.DeleteSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) {
return sandbox, nil
}
defer func() {
testingImpl.DeleteSandboxFunc = nil
}()
err = delete(context.Background(), sandbox.ID(), false)
assert.Nil(err)
}
func TestDeleteInvalidContainerType(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
rootPath, bundlePath := testConfigSetup(t)
defer os.RemoveAll(rootPath)
ociSpec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID())
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: "InvalidType",
},
State: types.ContainerState{
State: "created",
},
Spec: &ociSpec,
}, nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
// Delete an invalid container type
err = delete(context.Background(), sandbox.ID(), false)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
func TestDeleteSandboxRunning(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
rootPath, bundlePath := testConfigSetup(t)
defer os.RemoveAll(rootPath)
ociSpec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID())
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
},
State: types.ContainerState{
State: "running",
},
Spec: &ociSpec,
}, nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
// Delete on a running sandbox should fail
err = delete(context.Background(), sandbox.ID(), false)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
testingImpl.StatusSandboxFunc = func(ctx context.Context, sandboxID string) (vc.SandboxStatus, error) {
return vc.SandboxStatus{
ID: sandbox.ID(),
State: types.SandboxState{
State: types.StateRunning,
},
}, nil
}
testingImpl.StopSandboxFunc = func(ctx context.Context, sandboxID string, force bool) (vc.VCSandbox, error) {
return sandbox, nil
}
defer func() {
testingImpl.StatusSandboxFunc = nil
testingImpl.StopSandboxFunc = nil
}()
// Force delete a running sandbox
err = delete(context.Background(), sandbox.ID(), true)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
testingImpl.DeleteSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) {
return sandbox, nil
}
defer func() {
testingImpl.DeleteSandboxFunc = nil
}()
err = delete(context.Background(), sandbox.ID(), true)
assert.Nil(err)
}
func TestDeleteRunningContainer(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
sandbox.MockContainers = []*vcmock.Container{
{
MockID: testContainerID,
MockSandbox: sandbox,
},
}
rootPath, bundlePath := testConfigSetup(t)
defer os.RemoveAll(rootPath)
ociSpec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
path, err := createTempContainerIDMapping(sandbox.MockContainers[0].ID(), sandbox.MockContainers[0].ID())
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: sandbox.MockContainers[0].ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
},
State: types.ContainerState{
State: "running",
},
Spec: &ociSpec,
}, nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
// Delete on a running container should fail.
err = delete(context.Background(), sandbox.MockContainers[0].ID(), false)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
path, err = createTempContainerIDMapping(sandbox.MockContainers[0].ID(), sandbox.MockContainers[0].ID())
assert.NoError(err)
defer os.RemoveAll(path)
// force delete
err = delete(context.Background(), sandbox.MockContainers[0].ID(), true)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
testingImpl.StopContainerFunc = testStopContainerFuncReturnNil
defer func() {
testingImpl.StopContainerFunc = nil
}()
path, err = createTempContainerIDMapping(sandbox.MockContainers[0].ID(), sandbox.MockContainers[0].ID())
assert.NoError(err)
defer os.RemoveAll(path)
err = delete(context.Background(), sandbox.MockContainers[0].ID(), true)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
testingImpl.DeleteContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) {
return &vcmock.Container{}, nil
}
defer func() {
testingImpl.DeleteContainerFunc = nil
}()
path, err = createTempContainerIDMapping(sandbox.MockContainers[0].ID(), sandbox.MockContainers[0].ID())
assert.NoError(err)
defer os.RemoveAll(path)
err = delete(context.Background(), sandbox.MockContainers[0].ID(), true)
assert.Nil(err)
}
func TestDeleteContainer(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
sandbox.MockContainers = []*vcmock.Container{
{
MockID: testContainerID,
MockSandbox: sandbox,
},
}
rootPath, bundlePath := testConfigSetup(t)
defer os.RemoveAll(rootPath)
ociSpec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
path, err := createTempContainerIDMapping(sandbox.MockContainers[0].ID(), sandbox.MockContainers[0].ID())
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: sandbox.MockContainers[0].ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
},
State: types.ContainerState{
State: "ready",
},
Spec: &ociSpec,
}, nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
err = delete(context.Background(), sandbox.MockContainers[0].ID(), false)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
path, err = createTempContainerIDMapping(sandbox.MockContainers[0].ID(), sandbox.MockContainers[0].ID())
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StopContainerFunc = testStopContainerFuncReturnNil
defer func() {
testingImpl.StopContainerFunc = nil
}()
err = delete(context.Background(), sandbox.MockContainers[0].ID(), false)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
testingImpl.DeleteContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) {
return &vcmock.Container{}, nil
}
defer func() {
testingImpl.DeleteContainerFunc = nil
}()
path, err = createTempContainerIDMapping(sandbox.MockContainers[0].ID(), sandbox.MockContainers[0].ID())
assert.NoError(err)
defer os.RemoveAll(path)
err = delete(context.Background(), sandbox.MockContainers[0].ID(), false)
assert.Nil(err)
}
func TestDeleteCLIFunction(t *testing.T) {
assert := assert.New(t)
flagSet := &flag.FlagSet{}
ctx := createCLIContext(flagSet)
fn, ok := deleteCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
// no container id in the Metadata
err := fn(ctx)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
path, err := createTempContainerIDMapping("xyz", "xyz")
assert.NoError(err)
defer os.RemoveAll(path)
flagSet = flag.NewFlagSet("container-id", flag.ContinueOnError)
flagSet.Parse([]string{"xyz"})
ctx = createCLIContext(flagSet)
err = fn(ctx)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
}
func TestDeleteCLIFunctionSuccess(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
sandbox.MockContainers = []*vcmock.Container{
{
MockID: testContainerID,
MockSandbox: sandbox,
},
}
rootPath, bundlePath := testConfigSetup(t)
defer os.RemoveAll(rootPath)
ociSpec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID())
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
},
State: types.ContainerState{
State: "ready",
},
Spec: &ociSpec,
}, nil
}
testingImpl.StatusSandboxFunc = func(ctx context.Context, sandboxID string) (vc.SandboxStatus, error) {
return vc.SandboxStatus{
ID: sandbox.ID(),
State: types.SandboxState{
State: types.StateReady,
},
}, nil
}
testingImpl.StopSandboxFunc = func(ctx context.Context, sandboxID string, force bool) (vc.VCSandbox, error) {
return sandbox, nil
}
testingImpl.DeleteSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) {
return sandbox, nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
testingImpl.StopSandboxFunc = nil
testingImpl.DeleteSandboxFunc = nil
}()
flagSet := &flag.FlagSet{}
ctx := createCLIContext(flagSet)
fn, ok := deleteCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
err = fn(ctx)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
flagSet = flag.NewFlagSet("container-id", flag.ContinueOnError)
flagSet.Parse([]string{sandbox.ID()})
ctx = createCLIContext(flagSet)
assert.NotNil(ctx)
err = fn(ctx)
assert.NoError(err)
}
func TestRemoveCGroupsPath(t *testing.T) {
if tc.NotValid(ktu.NeedNonRoot()) {
t.Skip(ktu.TestDisabledNeedNonRoot)
}
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
dir := filepath.Join(tmpdir, "dir")
err = os.Mkdir(dir, testDirMode)
assert.NoError(err)
// make directory unreadable by non-root user
err = os.Chmod(tmpdir, 0000)
assert.NoError(err)
defer func() {
_ = os.Chmod(tmpdir, 0755)
}()
err = removeCgroupsPath(context.Background(), "foo", []string{dir})
assert.Error(err)
}

View File

@ -1,295 +0,0 @@
// Copyright (c) 2014,2015,2016,2017 Docker, Inc.
// Copyright (c) 2018 Huawei Corporation.
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"encoding/json"
"fmt"
"os"
"sync"
"time"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
type event struct {
Type string `json:"type"`
ID string `json:"id"`
Data interface{} `json:"data,omitempty"`
}
// stats is the runc specific stats structure for stability when encoding and decoding stats.
type stats struct {
CPU cpu `json:"cpu"`
Memory memory `json:"memory"`
Pids pids `json:"pids"`
Blkio blkio `json:"blkio"`
Hugetlb map[string]hugetlb `json:"hugetlb"`
IntelRdt intelRdt `json:"intel_rdt"`
}
type hugetlb struct {
Usage uint64 `json:"usage,omitempty"`
Max uint64 `json:"max,omitempty"`
Failcnt uint64 `json:"failcnt"`
}
type blkioEntry struct {
Major uint64 `json:"major,omitempty"`
Minor uint64 `json:"minor,omitempty"`
Op string `json:"op,omitempty"`
Value uint64 `json:"value,omitempty"`
}
type blkio struct {
IoServiceBytesRecursive []blkioEntry `json:"ioServiceBytesRecursive,omitempty"`
IoServicedRecursive []blkioEntry `json:"ioServicedRecursive,omitempty"`
IoQueuedRecursive []blkioEntry `json:"ioQueueRecursive,omitempty"`
IoServiceTimeRecursive []blkioEntry `json:"ioServiceTimeRecursive,omitempty"`
IoWaitTimeRecursive []blkioEntry `json:"ioWaitTimeRecursive,omitempty"`
IoMergedRecursive []blkioEntry `json:"ioMergedRecursive,omitempty"`
IoTimeRecursive []blkioEntry `json:"ioTimeRecursive,omitempty"`
SectorsRecursive []blkioEntry `json:"sectorsRecursive,omitempty"`
}
type pids struct {
Current uint64 `json:"current,omitempty"`
Limit uint64 `json:"limit,omitempty"`
}
type throttling struct {
Periods uint64 `json:"periods,omitempty"`
ThrottledPeriods uint64 `json:"throttledPeriods,omitempty"`
ThrottledTime uint64 `json:"throttledTime,omitempty"`
}
type cpuUsage struct {
// Units: nanoseconds.
Total uint64 `json:"total,omitempty"`
Percpu []uint64 `json:"percpu,omitempty"`
Kernel uint64 `json:"kernel"`
User uint64 `json:"user"`
}
type cpu struct {
Usage cpuUsage `json:"usage,omitempty"`
Throttling throttling `json:"throttling,omitempty"`
}
type memoryEntry struct {
Limit uint64 `json:"limit"`
Usage uint64 `json:"usage,omitempty"`
Max uint64 `json:"max,omitempty"`
Failcnt uint64 `json:"failcnt"`
}
type memory struct {
Cache uint64 `json:"cache,omitempty"`
Usage memoryEntry `json:"usage,omitempty"`
Swap memoryEntry `json:"swap,omitempty"`
Kernel memoryEntry `json:"kernel,omitempty"`
KernelTCP memoryEntry `json:"kernelTCP,omitempty"`
Raw map[string]uint64 `json:"raw,omitempty"`
}
type l3CacheInfo struct {
CbmMask string `json:"cbm_mask,omitempty"`
MinCbmBits uint64 `json:"min_cbm_bits,omitempty"`
NumClosids uint64 `json:"num_closids,omitempty"`
}
type intelRdt struct {
// The read-only L3 cache information
L3CacheInfo *l3CacheInfo `json:"l3_cache_info,omitempty"`
// The read-only L3 cache schema in root
L3CacheSchemaRoot string `json:"l3_cache_schema_root,omitempty"`
// The L3 cache schema in 'container_id' group
L3CacheSchema string `json:"l3_cache_schema,omitempty"`
}
var eventsCLICommand = cli.Command{
Name: "events",
Usage: "display container events such as OOM notifications, cpu, memory, and IO usage statistics",
ArgsUsage: `<container-id>
Where "<container-id>" is the name for the instance of the container.`,
Description: `The events command displays information about the container. By default the
information is displayed once every 5 seconds.`,
Flags: []cli.Flag{
cli.DurationFlag{
Name: "interval",
Value: 5 * time.Second,
Usage: "set the stats collection interval",
},
cli.BoolFlag{
Name: "stats",
Usage: "display the container's stats then exit",
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
span, _ := katautils.Trace(ctx, "events")
defer span.Finish()
containerID := context.Args().First()
if containerID == "" {
return fmt.Errorf("container id cannot be empty")
}
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(ctx, kataLog)
span.SetTag("container", containerID)
duration := context.Duration("interval")
if duration <= 0 {
return fmt.Errorf("duration interval must be greater than 0")
}
status, sandboxID, err := getExistingContainerInfo(ctx, containerID)
if err != nil {
return err
}
containerID = status.ID
kataLog = kataLog.WithFields(logrus.Fields{
"container": containerID,
"sandbox": sandboxID,
})
setExternalLoggers(ctx, kataLog)
span.SetTag("container", containerID)
span.SetTag("sandbox", sandboxID)
if status.State.State == types.StateStopped {
return fmt.Errorf("container with id %s is not running", status.ID)
}
var (
events = make(chan *event, 1024)
group = &sync.WaitGroup{}
)
group.Add(1)
go func() {
defer group.Done()
enc := json.NewEncoder(os.Stdout)
for e := range events {
if err := enc.Encode(e); err != nil {
logrus.Error(err)
}
}
}()
if context.Bool("stats") {
s, err := vci.StatsContainer(ctx, sandboxID, containerID)
if err != nil {
return err
}
events <- &event{Type: "stats", ID: status.ID, Data: convertVirtcontainerStats(&s)}
close(events)
group.Wait()
return nil
}
go func() {
for range time.Tick(context.Duration("interval")) {
s, err := vci.StatsContainer(ctx, sandboxID, containerID)
if err != nil {
logrus.Error(err)
continue
}
events <- &event{Type: "stats", ID: status.ID, Data: convertVirtcontainerStats(&s)}
}
}()
group.Wait()
return nil
},
}
func convertVirtcontainerStats(containerStats *vc.ContainerStats) *stats {
cg := containerStats.CgroupStats
if cg == nil {
return nil
}
var s stats
s.Pids.Current = cg.PidsStats.Current
s.Pids.Limit = cg.PidsStats.Limit
s.CPU.Usage.Kernel = cg.CPUStats.CPUUsage.UsageInKernelmode
s.CPU.Usage.User = cg.CPUStats.CPUUsage.UsageInUsermode
s.CPU.Usage.Total = cg.CPUStats.CPUUsage.TotalUsage
s.CPU.Usage.Percpu = cg.CPUStats.CPUUsage.PercpuUsage
s.CPU.Throttling.Periods = cg.CPUStats.ThrottlingData.Periods
s.CPU.Throttling.ThrottledPeriods = cg.CPUStats.ThrottlingData.ThrottledPeriods
s.CPU.Throttling.ThrottledTime = cg.CPUStats.ThrottlingData.ThrottledTime
s.Memory.Cache = cg.MemoryStats.Cache
s.Memory.Kernel = convertMemoryEntry(cg.MemoryStats.KernelUsage)
s.Memory.KernelTCP = convertMemoryEntry(cg.MemoryStats.KernelTCPUsage)
s.Memory.Swap = convertMemoryEntry(cg.MemoryStats.SwapUsage)
s.Memory.Usage = convertMemoryEntry(cg.MemoryStats.Usage)
s.Memory.Raw = cg.MemoryStats.Stats
s.Blkio.IoServiceBytesRecursive = convertBlkioEntry(cg.BlkioStats.IoServiceBytesRecursive)
s.Blkio.IoServicedRecursive = convertBlkioEntry(cg.BlkioStats.IoServicedRecursive)
s.Blkio.IoQueuedRecursive = convertBlkioEntry(cg.BlkioStats.IoQueuedRecursive)
s.Blkio.IoServiceTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoServiceTimeRecursive)
s.Blkio.IoWaitTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoWaitTimeRecursive)
s.Blkio.IoMergedRecursive = convertBlkioEntry(cg.BlkioStats.IoMergedRecursive)
s.Blkio.IoTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoTimeRecursive)
s.Blkio.SectorsRecursive = convertBlkioEntry(cg.BlkioStats.SectorsRecursive)
s.Hugetlb = make(map[string]hugetlb)
for k, v := range cg.HugetlbStats {
s.Hugetlb[k] = convertHugtlb(v)
}
return &s
}
func convertHugtlb(c vc.HugetlbStats) hugetlb {
return hugetlb{
Usage: c.Usage,
Max: c.MaxUsage,
Failcnt: c.Failcnt,
}
}
func convertMemoryEntry(c vc.MemoryData) memoryEntry {
return memoryEntry{
Limit: c.Limit,
Usage: c.Usage,
Max: c.MaxUsage,
Failcnt: c.Failcnt,
}
}
func convertBlkioEntry(c []vc.BlkioStatEntry) []blkioEntry {
var out []blkioEntry
for _, e := range c {
out = append(out, blkioEntry{
Major: e.Major,
Minor: e.Minor,
Op: e.Op,
Value: e.Value,
})
}
return out
}

View File

@ -1,144 +0,0 @@
// Copyright (c) 2018 Huawei Corporation.
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"flag"
"os"
"testing"
"time"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
vcAnnotations "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/vcmock"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli"
)
func TestEventsCliAction(t *testing.T) {
assert := assert.New(t)
// get Action function
actionFunc, ok := eventsCLICommand.Action.(func(ctx *cli.Context) error)
assert.True(ok)
flagSet := flag.NewFlagSet("events", flag.ContinueOnError)
// create a new fake context
ctx := createCLIContext(flagSet)
err := actionFunc(ctx)
assert.Error(err, "Missing container ID")
}
func TestEventsCLIFailure(t *testing.T) {
assert := assert.New(t)
flagSet := flag.NewFlagSet("events", flag.ContinueOnError)
ctx := createCLIContext(flagSet)
actionFunc, ok := eventsCLICommand.Action.(func(ctx *cli.Context) error)
assert.True(ok)
// missing container ID
err := actionFunc(ctx)
assert.Error(err)
// interval is negative
flagSet.Parse([]string{testContainerID})
flagSet.Duration("interval", (-1)*time.Second, "")
ctx = createCLIContext(flagSet)
err = actionFunc(ctx)
assert.Error(err)
// interval is zero
flagSet = flag.NewFlagSet("events", flag.ContinueOnError)
flagSet.Parse([]string{testContainerID})
flagSet.Duration("interval", 0*time.Second, "")
ctx = createCLIContext(flagSet)
err = actionFunc(ctx)
assert.Error(err)
// not running
sandbox := &vcmock.Sandbox{
MockID: testContainerID,
}
sandbox.MockContainers = []*vcmock.Container{
{
MockID: sandbox.ID(),
MockSandbox: sandbox,
},
}
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
},
}, nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
err = actionFunc(ctx)
assert.Error(err)
}
func TestEventsCLISuccessful(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testContainerID,
}
sandbox.MockContainers = []*vcmock.Container{
{
MockID: sandbox.ID(),
MockSandbox: sandbox,
},
}
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
},
State: types.ContainerState{
State: types.StateRunning,
},
}, nil
}
testingImpl.StatsContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStats, error) {
return vc.ContainerStats{}, nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
testingImpl.StatsContainerFunc = nil
}()
path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID())
assert.NoError(err)
defer os.RemoveAll(path)
actionFunc, ok := eventsCLICommand.Action.(func(ctx *cli.Context) error)
assert.True(ok)
flagSet := flag.NewFlagSet("events", flag.ContinueOnError)
flagSet.Parse([]string{testContainerID})
flagSet.Duration("interval", 5*time.Second, "")
flagSet.Bool("stats", true, "")
ctx := createCLIContext(flagSet)
err = actionFunc(ctx)
assert.NoError(err)
}

View File

@ -1,291 +0,0 @@
// Copyright (c) 2014,2015,2016 Docker, Inc.
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"syscall"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/oci"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/urfave/cli"
)
type execParams struct {
ociProcess specs.Process
cID string
pidFile string
console string
consoleSock string
processLabel string
detach bool
noSubreaper bool
}
var execCLICommand = cli.Command{
Name: "exec",
Usage: "Execute new process inside the container",
ArgsUsage: `<container-id> <command> [command options] || -p process.json <container-id>
<container-id> is the name for the instance of the container and <command>
is the command to be executed in the container. <command> can't be empty
unless a "-p" flag provided.
EXAMPLE:
If the container is configured to run the linux ps command the following
will output a list of processes running in the container:
# ` + name + ` <container-id> ps`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "console",
Usage: "path to a pseudo terminal",
},
cli.StringFlag{
Name: "console-socket",
Value: "",
Usage: "path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal",
},
cli.StringFlag{
Name: "cwd",
Usage: "current working directory in the container",
},
cli.StringSliceFlag{
Name: "env, e",
Usage: "set environment variables",
},
cli.BoolFlag{
Name: "tty, t",
Usage: "allocate a pseudo-TTY",
},
cli.StringFlag{
Name: "user, u",
Usage: "UID (format: <uid>[:<gid>])",
},
cli.StringFlag{
Name: "process, p",
Usage: "path to the process.json",
},
cli.BoolFlag{
Name: "detach,d",
Usage: "detach from the container's process",
},
cli.StringFlag{
Name: "pid-file",
Value: "",
Usage: "specify the file to write the process id to",
},
cli.StringFlag{
Name: "process-label",
Usage: "set the asm process label for the process commonly used with selinux",
},
cli.StringFlag{
Name: "apparmor",
Usage: "set the apparmor profile for the process",
},
cli.BoolFlag{
Name: "no-new-privs",
Usage: "set the no new privileges value for the process",
},
cli.StringSliceFlag{
Name: "cap, c",
Value: &cli.StringSlice{},
Usage: "add a capability to the bounding set for the process",
},
cli.BoolFlag{
Name: "no-subreaper",
Usage: "disable the use of the subreaper used to reap reparented processes",
Hidden: true,
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
return execute(ctx, context)
},
}
func generateExecParams(context *cli.Context, specProcess *specs.Process) (execParams, error) {
ctxArgs := context.Args()
params := execParams{
cID: ctxArgs.First(),
pidFile: context.String("pid-file"),
console: context.String("console"),
consoleSock: context.String("console-socket"),
detach: context.Bool("detach"),
processLabel: context.String("process-label"),
noSubreaper: context.Bool("no-subreaper"),
}
if context.String("process") != "" {
var ociProcess specs.Process
fileContent, err := ioutil.ReadFile(context.String("process"))
if err != nil {
return execParams{}, err
}
if err := json.Unmarshal(fileContent, &ociProcess); err != nil {
return execParams{}, err
}
params.ociProcess = ociProcess
} else {
params.ociProcess = *specProcess
// Override terminal
if context.IsSet("tty") {
params.ociProcess.Terminal = context.Bool("tty")
}
// Override user
if context.String("user") != "" {
params.ociProcess.User = specs.User{
// This field is a Windows-only field
// according to the specification. However, it
// is abused here to allow the username
// specified in the OCI runtime configuration
// file to be overridden by a CLI request.
Username: context.String("user"),
}
}
// Override env
params.ociProcess.Env = append(params.ociProcess.Env, context.StringSlice("env")...)
// Override cwd
if context.String("cwd") != "" {
params.ociProcess.Cwd = context.String("cwd")
}
// Override no-new-privs
if context.IsSet("no-new-privs") {
params.ociProcess.NoNewPrivileges = context.Bool("no-new-privs")
}
// Override apparmor
if context.String("apparmor") != "" {
params.ociProcess.ApparmorProfile = context.String("apparmor")
}
params.ociProcess.Args = ctxArgs.Tail()
}
return params, nil
}
func execute(ctx context.Context, context *cli.Context) error {
span, ctx := katautils.Trace(ctx, "execute")
defer span.Finish()
containerID := context.Args().First()
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(ctx, kataLog)
span.SetTag("container", containerID)
status, sandboxID, err := getExistingContainerInfo(ctx, containerID)
if err != nil {
return err
}
kataLog = kataLog.WithField("sandbox", sandboxID)
setExternalLoggers(ctx, kataLog)
span.SetTag("sandbox", sandboxID)
// Retrieve OCI spec configuration.
ociSpec, err := oci.GetOCIConfig(status)
if err != nil {
return err
}
params, err := generateExecParams(context, ociSpec.Process)
if err != nil {
return err
}
params.cID = status.ID
containerID = params.cID
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(ctx, kataLog)
span.SetTag("container", containerID)
// container MUST be ready or running.
if status.State.State != types.StateReady &&
status.State.State != types.StateRunning {
return fmt.Errorf("Container %s is not ready or running",
params.cID)
}
envVars, err := oci.EnvVars(params.ociProcess.Env)
if err != nil {
return err
}
consolePath, err := setupConsole(params.console, params.consoleSock)
if err != nil {
return err
}
user := fmt.Sprintf("%d:%d", params.ociProcess.User.UID, params.ociProcess.User.GID)
if params.ociProcess.User.Username != "" {
user = params.ociProcess.User.Username
}
cmd := types.Cmd{
Args: params.ociProcess.Args,
Envs: envVars,
WorkDir: params.ociProcess.Cwd,
User: user,
Interactive: params.ociProcess.Terminal,
Console: consolePath,
Detach: noNeedForOutput(params.detach, params.ociProcess.Terminal),
}
_, _, process, err := vci.EnterContainer(ctx, sandboxID, params.cID, cmd)
if err != nil {
return err
}
// Creation of PID file has to be the last thing done in the exec
// because containerd considers the exec to have finished starting
// after this file is created.
if err := createPIDFile(ctx, params.pidFile, process.Pid); err != nil {
return err
}
if params.detach {
return nil
}
p, err := os.FindProcess(process.Pid)
if err != nil {
return err
}
ps, err := p.Wait()
if err != nil {
return fmt.Errorf("Process state %s, container info %+v: %v",
ps.String(), status, err)
}
// Exit code has to be forwarded in this case.
return cli.NewExitError("", ps.Sys().(syscall.WaitStatus).ExitStatus())
}

View File

@ -1,787 +0,0 @@
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"flag"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"testing"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
vcAnnotations "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/compatoci"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/vcmock"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
)
func TestExecCLIFunction(t *testing.T) {
assert := assert.New(t)
flagSet := &flag.FlagSet{}
ctx := createCLIContext(flagSet)
fn, ok := startCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
// no container-id in the Metadata
err := fn(ctx)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
path, err := createTempContainerIDMapping("xyz", "xyz")
assert.NoError(err)
defer os.RemoveAll(path)
// pass container-id
flagSet = flag.NewFlagSet("container-id", flag.ContinueOnError)
flagSet.Parse([]string{"xyz"})
ctx = createCLIContext(flagSet)
err = fn(ctx)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
}
func TestExecuteErrors(t *testing.T) {
assert := assert.New(t)
flagSet := flag.NewFlagSet("", 0)
ctx := createCLIContext(flagSet)
// missing container id
err := execute(context.Background(), ctx)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
// StatusSandbox error
flagSet.Parse([]string{testContainerID})
err = execute(context.Background(), ctx)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
// Config path missing in annotations
annotations := map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
}
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, types.ContainerState{}, annotations, &specs.Spec{Process: &specs.Process{}}), nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
err = execute(context.Background(), ctx)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
// Container state undefined
rootPath, bundlePath := testConfigSetup(t)
defer os.RemoveAll(rootPath)
ociSpec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
annotations = map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
}
containerState := types.ContainerState{}
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, containerState, annotations, &ociSpec), nil
}
err = execute(context.Background(), ctx)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
// Container paused
containerState = types.ContainerState{
State: types.StatePaused,
}
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, containerState, annotations, &ociSpec), nil
}
err = execute(context.Background(), ctx)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
// Container stopped
containerState = types.ContainerState{
State: types.StateStopped,
}
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, containerState, annotations, &ociSpec), nil
}
err = execute(context.Background(), ctx)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
func TestExecuteErrorReadingProcessJson(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
// non-existent path
processPath := filepath.Join(tmpdir, "process.json")
flagSet := flag.NewFlagSet("", 0)
flagSet.String("process", processPath, "")
flagSet.Parse([]string{testContainerID})
ctx := createCLIContext(flagSet)
rootPath, bundlePath := testConfigSetup(t)
defer os.RemoveAll(rootPath)
ociSpec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
annotations := map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
}
state := types.ContainerState{
State: types.StateRunning,
}
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, annotations, &ociSpec), nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
// Note: flags can only be tested with the CLI command function
fn, ok := execCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
err = fn(ctx)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
func TestExecuteErrorOpeningConsole(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
consoleSock := filepath.Join(tmpdir, "console-sock")
flagSet := flag.NewFlagSet("", 0)
flagSet.String("console-socket", consoleSock, "")
flagSet.Parse([]string{testContainerID})
ctx := createCLIContext(flagSet)
rootPath, bundlePath := testConfigSetup(t)
defer os.RemoveAll(rootPath)
ociSpec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
annotations := map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
}
state := types.ContainerState{
State: types.StateRunning,
}
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, annotations, &ociSpec), nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
// Note: flags can only be tested with the CLI command function
fn, ok := execCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
err = fn(ctx)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
func testExecParamsSetup(t *testing.T, pidFilePath, consolePath string, detach bool) *flag.FlagSet {
flagSet := flag.NewFlagSet("", 0)
flagSet.String("pid-file", pidFilePath, "")
flagSet.String("console", consolePath, "")
flagSet.String("console-socket", "", "")
flagSet.Bool("detach", detach, "")
flagSet.String("process-label", "testlabel", "")
flagSet.Bool("no-subreaper", false, "")
return flagSet
}
func TestExecuteWithFlags(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
pidFilePath := filepath.Join(tmpdir, "pid")
consolePath := "/dev/ptmx"
flagSet := testExecParamsSetup(t, pidFilePath, consolePath, false)
flagSet.String("user", "root", "")
flagSet.String("cwd", "/home/root", "")
flagSet.String("apparmor", "/tmp/profile", "")
flagSet.Bool("no-new-privs", false, "")
flagSet.Parse([]string{testContainerID, "/tmp/foo"})
ctx := createCLIContext(flagSet)
rootPath, bundlePath := testConfigSetup(t)
defer os.RemoveAll(rootPath)
ociSpec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
annotations := map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
}
state := types.ContainerState{
State: types.StateRunning,
}
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, annotations, &ociSpec), nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
fn, ok := execCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
// EnterContainer error
err = fn(ctx)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
testingImpl.EnterContainerFunc = func(ctx context.Context, sandboxID, containerID string, cmd types.Cmd) (vc.VCSandbox, vc.VCContainer, *vc.Process, error) {
return &vcmock.Sandbox{}, &vcmock.Container{}, &vc.Process{}, nil
}
defer func() {
testingImpl.EnterContainerFunc = nil
os.Remove(pidFilePath)
}()
// Process not running error
err = fn(ctx)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
os.Remove(pidFilePath)
// Process ran and exited successfully
testingImpl.EnterContainerFunc = func(ctx context.Context, sandboxID, containerID string, cmd types.Cmd) (vc.VCSandbox, vc.VCContainer, *vc.Process, error) {
// create a fake container process
workload := []string{"cat", "/dev/null"}
command := exec.Command(workload[0], workload[1:]...)
err := command.Start()
assert.NoError(err, "Unable to start process %v: %s", workload, err)
vcProcess := vc.Process{}
vcProcess.Pid = command.Process.Pid
return &vcmock.Sandbox{}, &vcmock.Container{}, &vcProcess, nil
}
defer func() {
testingImpl.EnterContainerFunc = nil
os.Remove(pidFilePath)
}()
// Should get an exit code when run in non-detached mode.
err = fn(ctx)
_, ok = err.(*cli.ExitError)
assert.True(ok, true, "Exit code not received for fake workload process")
}
func TestExecuteWithFlagsDetached(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
pidFilePath := filepath.Join(tmpdir, "pid")
consolePath := "/dev/ptmx"
detach := true
flagSet := testExecParamsSetup(t, pidFilePath, consolePath, detach)
flagSet.Parse([]string{testContainerID, "/tmp/foo"})
ctx := createCLIContext(flagSet)
rootPath, bundlePath := testConfigSetup(t)
defer os.RemoveAll(rootPath)
ociSpec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
annotations := map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
}
state := types.ContainerState{
State: types.StateRunning,
}
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, annotations, &ociSpec), nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
testingImpl.EnterContainerFunc = func(ctx context.Context, sandboxID, containerID string, cmd types.Cmd) (vc.VCSandbox, vc.VCContainer, *vc.Process, error) {
// create a fake container process
workload := []string{"cat", "/dev/null"}
command := exec.Command(workload[0], workload[1:]...)
err := command.Start()
assert.NoError(err, "Unable to start process %v: %s", workload, err)
vcProcess := vc.Process{}
vcProcess.Pid = command.Process.Pid
return &vcmock.Sandbox{}, &vcmock.Container{}, &vcProcess, nil
}
defer func() {
testingImpl.EnterContainerFunc = nil
os.Remove(pidFilePath)
}()
fn, ok := execCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
err = fn(ctx)
assert.NoError(err)
}
func TestExecuteWithInvalidProcessJson(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
pidFilePath := filepath.Join(tmpdir, "pid")
consolePath := "/dev/ptmx"
detach := false
flagSet := testExecParamsSetup(t, pidFilePath, consolePath, detach)
processPath := filepath.Join(tmpdir, "process.json")
flagSet.String("process", processPath, "")
f, err := os.OpenFile(processPath, os.O_RDWR|os.O_CREATE, testFileMode)
assert.NoError(err)
// invalidate the JSON
_, err = f.WriteString("{")
assert.NoError(err)
f.Close()
defer os.Remove(processPath)
flagSet.Parse([]string{testContainerID})
ctx := createCLIContext(flagSet)
rootPath, bundlePath := testConfigSetup(t)
defer os.RemoveAll(rootPath)
ociSpec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
annotations := map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
}
state := types.ContainerState{
State: types.StateRunning,
}
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, annotations, &ociSpec), nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
fn, ok := execCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
err = fn(ctx)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
func TestExecuteWithValidProcessJson(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
pidFilePath := filepath.Join(tmpdir, "pid")
consolePath := "/dev/ptmx"
flagSet := testExecParamsSetup(t, pidFilePath, consolePath, false)
processPath := filepath.Join(tmpdir, "process.json")
flagSet.String("process", processPath, "")
flagSet.Parse([]string{testContainerID, "/tmp/foo"})
ctx := createCLIContext(flagSet)
rootPath, bundlePath := testConfigSetup(t)
defer os.RemoveAll(rootPath)
ociSpec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
annotations := map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
}
state := types.ContainerState{
State: types.StateRunning,
}
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, annotations, &ociSpec), nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
processJSON := `{
"consoleSize": {
"height": 15,
"width": 15
},
"terminal": true,
"user": {
"uid": 0,
"gid": 0
},
"args": [
"sh"
],
"env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"TERM=xterm"
],
"cwd": "/"
}`
f, err := os.OpenFile(processPath, os.O_RDWR|os.O_CREATE, testFileMode)
assert.NoError(err)
_, err = f.WriteString(processJSON)
assert.NoError(err)
f.Close()
defer os.Remove(processPath)
workload := []string{"cat", "/dev/null"}
testingImpl.EnterContainerFunc = func(ctx context.Context, sandboxID, containerID string, cmd types.Cmd) (vc.VCSandbox, vc.VCContainer, *vc.Process, error) {
// create a fake container process
command := exec.Command(workload[0], workload[1:]...)
err := command.Start()
assert.NoError(err, "Unable to start process %v: %s", workload, err)
vcProcess := vc.Process{}
vcProcess.Pid = command.Process.Pid
return &vcmock.Sandbox{}, &vcmock.Container{}, &vcProcess, nil
}
defer func() {
testingImpl.EnterContainerFunc = nil
os.Remove(pidFilePath)
}()
fn, ok := execCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
err = fn(ctx)
exitErr, ok := err.(*cli.ExitError)
assert.True(ok, true, "Exit code not received for fake workload process")
assert.Equal(exitErr.ExitCode(), 0, "Exit code should have been 0 for fake workload %s", workload)
}
func TestExecuteWithEmptyEnvironmentValue(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
pidFilePath := filepath.Join(tmpdir, "pid")
consolePath := "/dev/ptmx"
flagSet := testExecParamsSetup(t, pidFilePath, consolePath, false)
processPath := filepath.Join(tmpdir, "process.json")
flagSet.String("process", processPath, "")
flagSet.Parse([]string{testContainerID})
ctx := createCLIContext(flagSet)
rootPath, bundlePath := testConfigSetup(t)
defer os.RemoveAll(rootPath)
ociSpec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
annotations := map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
}
state := types.ContainerState{
State: types.StateRunning,
}
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, annotations, &ociSpec), nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
processJSON := `{
"consoleSize": {
"height": 15,
"width": 15
},
"terminal": true,
"user": {
"uid": 0,
"gid": 0
},
"args": [
"sh"
],
"env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"TERM="
],
"cwd": "/"
}`
f, err := os.OpenFile(processPath, os.O_RDWR|os.O_CREATE, testFileMode)
assert.NoError(err)
_, err = f.WriteString(processJSON)
assert.NoError(err)
f.Close()
defer os.Remove(processPath)
workload := []string{"cat", "/dev/null"}
testingImpl.EnterContainerFunc = func(ctx context.Context, sandboxID, containerID string, cmd types.Cmd) (vc.VCSandbox, vc.VCContainer, *vc.Process, error) {
// create a fake container process
command := exec.Command(workload[0], workload[1:]...)
err := command.Start()
assert.NoError(err, "Unable to start process %v: %s", workload, err)
vcProcess := vc.Process{}
vcProcess.Pid = command.Process.Pid
return &vcmock.Sandbox{}, &vcmock.Container{}, &vcProcess, nil
}
defer func() {
testingImpl.EnterContainerFunc = nil
os.Remove(pidFilePath)
}()
fn, ok := execCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
// vcAnnotations.EnvVars error due to incorrect environment
err = fn(ctx)
exitErr, ok := err.(*cli.ExitError)
assert.True(ok, true, "Exit code not received for fake workload process")
assert.Equal(exitErr.ExitCode(), 0, "Exit code should have been 0 for empty environment variable value")
}
func TestGenerateExecParams(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
pidFilePath := filepath.Join(tmpdir, "pid")
consolePath := "/dev/ptmx"
consoleSocket := "/tmp/console-sock"
processLabel := "testlabel"
user := "root"
cwd := "cwd"
apparmor := "apparmorProf"
flagSet := flag.NewFlagSet("", 0)
flagSet.String("pid-file", pidFilePath, "")
flagSet.String("console", consolePath, "")
flagSet.String("console-socket", consoleSocket, "")
flagSet.String("process-label", processLabel, "")
flagSet.String("user", user, "")
flagSet.String("cwd", cwd, "")
flagSet.String("apparmor", apparmor, "")
ctx := createCLIContext(flagSet)
process := &specs.Process{}
params, err := generateExecParams(ctx, process)
assert.NoError(err)
assert.Equal(params.pidFile, pidFilePath)
assert.Equal(params.console, consolePath)
assert.Equal(params.consoleSock, consoleSocket)
assert.Equal(params.processLabel, processLabel)
assert.Equal(params.noSubreaper, false)
assert.Equal(params.detach, false)
assert.Equal(params.ociProcess.Terminal, false)
assert.Equal(params.ociProcess.User.UID, uint32(0))
assert.Equal(params.ociProcess.User.GID, uint32(0))
assert.Equal(params.ociProcess.Cwd, cwd)
assert.Equal(params.ociProcess.ApparmorProfile, apparmor)
}
func TestGenerateExecParamsWithProcessJsonFile(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
pidFilePath := filepath.Join(tmpdir, "pid")
consolePath := "/dev/ptmx"
consoleSocket := "/tmp/console-sock"
detach := true
processLabel := "testlabel"
flagSet := flag.NewFlagSet("", 0)
flagSet.String("pid-file", pidFilePath, "")
flagSet.String("console", consolePath, "")
flagSet.String("console-socket", consoleSocket, "")
flagSet.Bool("detach", detach, "")
flagSet.String("process-label", processLabel, "")
processPath := filepath.Join(tmpdir, "process.json")
flagSet.String("process", processPath, "")
flagSet.Parse([]string{testContainerID})
ctx := createCLIContext(flagSet)
processJSON := `{
"consoleSize": {
"height": 15,
"width": 15
},
"terminal": true,
"user": {
"uid": 0,
"gid": 0
},
"args": [
"sh"
],
"env": [
"TERM=xterm",
"foo=bar"
],
"cwd": "/"
}`
f, err := os.OpenFile(processPath, os.O_RDWR|os.O_CREATE, testFileMode)
assert.NoError(err)
_, err = f.WriteString(processJSON)
assert.NoError(err)
f.Close()
defer os.Remove(processPath)
process := &specs.Process{}
params, err := generateExecParams(ctx, process)
assert.NoError(err)
assert.Equal(params.pidFile, pidFilePath)
assert.Equal(params.console, consolePath)
assert.Equal(params.consoleSock, consoleSocket)
assert.Equal(params.processLabel, processLabel)
assert.Equal(params.noSubreaper, false)
assert.Equal(params.detach, detach)
assert.Equal(params.ociProcess.Terminal, true)
assert.Equal(params.ociProcess.ConsoleSize.Height, uint(15))
assert.Equal(params.ociProcess.ConsoleSize.Width, uint(15))
assert.Equal(params.ociProcess.User.UID, uint32(0))
assert.Equal(params.ociProcess.User.GID, uint32(0))
assert.Equal(params.ociProcess.Cwd, "/")
assert.Equal(params.ociProcess.Env[0], "TERM=xterm")
assert.Equal(params.ociProcess.Env[1], "foo=bar")
}

View File

@ -1,136 +0,0 @@
// +build cgo,linux
// Copyright (c) 2014,2015,2016 Docker, Inc.
// Copyright (c) 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"fmt"
"time"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var kataOverheadCLICommand = cli.Command{
Name: "kata-overhead",
Usage: "provides kata overhead at sandbox level",
ArgsUsage: `<sandbox-id> [sandbox-id...]
<sandbox-id> is your name for the instance of the sandbox.`,
Description: `The kata-overhead command shows the overhead of a running Kata sandbox. Overhead
is calculated as the sum of pod resource utilization as measured on host cgroup minus the total
container usage measured inside the Kata guest for each container's cgroup.`,
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
args := context.Args()
if !args.Present() {
return fmt.Errorf("Missing container ID, should at least provide one")
}
for _, cID := range []string(args) {
if err := overhead(ctx, cID); err != nil {
return err
}
}
return nil
},
}
func overhead(ctx context.Context, containerID string) error {
span, _ := katautils.Trace(ctx, "overhead")
defer span.Finish()
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(ctx, kataLog)
span.SetTag("container", containerID)
status, sandboxID, err := getExistingContainerInfo(ctx, containerID)
if err != nil {
return err
}
containerID = status.ID
kataLog = kataLog.WithFields(logrus.Fields{
"container": containerID,
"sandbox": sandboxID,
})
setExternalLoggers(ctx, kataLog)
span.SetTag("container", containerID)
span.SetTag("sandbox", sandboxID)
if status.State.State == types.StateStopped {
return fmt.Errorf("container with id %s is not running", status.ID)
}
initTime := time.Now().UnixNano()
initialSandboxStats, initialContainerStats, err := vci.StatsSandbox(ctx, sandboxID)
if err != nil {
return err
}
hostInitCPU := initialSandboxStats.CgroupStats.CPUStats.CPUUsage.TotalUsage
guestInitCPU := uint64(0)
for _, cs := range initialContainerStats {
guestInitCPU += cs.CgroupStats.CPUStats.CPUUsage.TotalUsage
}
// Wait for 1 second to calculate CPU usage
time.Sleep(time.Second * 1)
finishtTime := time.Now().UnixNano()
finishSandboxStats, finishContainersStats, err := vci.StatsSandbox(ctx, sandboxID)
if err != nil {
return err
}
hostFinalCPU := finishSandboxStats.CgroupStats.CPUStats.CPUUsage.TotalUsage
guestFinalCPU := uint64(0)
for _, cs := range finishContainersStats {
guestFinalCPU += cs.CgroupStats.CPUStats.CPUUsage.TotalUsage
}
var guestMemoryUsage uint64
for _, cs := range finishContainersStats {
guestMemoryUsage += cs.CgroupStats.MemoryStats.Usage.Usage
}
hostMemoryUsage := finishSandboxStats.CgroupStats.MemoryStats.Usage.Usage
deltaTime := finishtTime - initTime
cpuUsageGuest := float64(guestFinalCPU-guestInitCPU) / float64(deltaTime) * 100
cpuUsageHost := float64(hostFinalCPU-hostInitCPU) / float64(deltaTime) * 100
fmt.Printf("Sandbox overhead for container: %s\n", containerID)
fmt.Printf("cpu_overhead=%f\n", cpuUsageHost-cpuUsageGuest)
fmt.Printf("memory_overhead_bytes=%d\n\n", hostMemoryUsage-guestMemoryUsage)
fmt.Printf(" --CPU details--\n")
fmt.Printf("cpu_host=%f\n", cpuUsageHost)
fmt.Printf("\tcpu_host_init=%d\n", hostInitCPU)
fmt.Printf("\tcpu_host_final=%d\n", hostFinalCPU)
fmt.Printf("cpu_guest=%f\n", cpuUsageGuest)
fmt.Printf("\tcpu_guest_init=%d\n", guestInitCPU)
fmt.Printf("\tcpu_guest_final=%d\n", guestFinalCPU)
fmt.Printf("Number of available vCPUs=%d\n", finishSandboxStats.Cpus)
fmt.Printf(" --Memory details--\n")
fmt.Printf("memory_host_bytes=%d\n", hostMemoryUsage)
fmt.Printf("memory_guest_bytes=%d\n\n", guestMemoryUsage)
return nil
}

View File

@ -1,193 +0,0 @@
// Copyright (c) 2014,2015,2016 Docker, Inc.
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"fmt"
"strconv"
"syscall"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/oci"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var killCLICommand = cli.Command{
Name: "kill",
Usage: "Kill sends signals to the container's init process",
ArgsUsage: `<container-id> [signal]
<container-id> is the name for the instance of the container
[signal] is the signal to be sent to the init process (default: SIGTERM)
EXAMPLE:
If the container id is "ubuntu01" the following will send a "KILL" signal
to the init process of the "ubuntu01" container:
# ` + name + ` kill ubuntu01 KILL`,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "all, a",
Usage: "send the specified signal to all processes inside the container",
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
args := context.Args()
if !args.Present() {
return fmt.Errorf("Missing container ID")
}
// If signal is provided, it has to be the second argument.
signal := args.Get(1)
if signal == "" {
signal = "SIGTERM"
}
return kill(ctx, args.First(), signal, context.Bool("all"))
},
}
var signalList = map[string]syscall.Signal{
"SIGABRT": syscall.SIGABRT,
"SIGALRM": syscall.SIGALRM,
"SIGBUS": syscall.SIGBUS,
"SIGCHLD": syscall.SIGCHLD,
"SIGCLD": syscall.SIGCLD,
"SIGCONT": syscall.SIGCONT,
"SIGFPE": syscall.SIGFPE,
"SIGHUP": syscall.SIGHUP,
"SIGILL": syscall.SIGILL,
"SIGINT": syscall.SIGINT,
"SIGIO": syscall.SIGIO,
"SIGIOT": syscall.SIGIOT,
"SIGKILL": syscall.SIGKILL,
"SIGPIPE": syscall.SIGPIPE,
"SIGPOLL": syscall.SIGPOLL,
"SIGPROF": syscall.SIGPROF,
"SIGPWR": syscall.SIGPWR,
"SIGQUIT": syscall.SIGQUIT,
"SIGSEGV": syscall.SIGSEGV,
"SIGSTKFLT": syscall.SIGSTKFLT,
"SIGSTOP": syscall.SIGSTOP,
"SIGSYS": syscall.SIGSYS,
"SIGTERM": syscall.SIGTERM,
"SIGTRAP": syscall.SIGTRAP,
"SIGTSTP": syscall.SIGTSTP,
"SIGTTIN": syscall.SIGTTIN,
"SIGTTOU": syscall.SIGTTOU,
"SIGUNUSED": syscall.SIGUNUSED,
"SIGURG": syscall.SIGURG,
"SIGUSR1": syscall.SIGUSR1,
"SIGUSR2": syscall.SIGUSR2,
"SIGVTALRM": syscall.SIGVTALRM,
"SIGWINCH": syscall.SIGWINCH,
"SIGXCPU": syscall.SIGXCPU,
"SIGXFSZ": syscall.SIGXFSZ,
}
func kill(ctx context.Context, containerID, signal string, all bool) error {
span, _ := katautils.Trace(ctx, "kill")
defer span.Finish()
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(ctx, kataLog)
span.SetTag("container", containerID)
// Checks the MUST and MUST NOT from OCI runtime specification
status, sandboxID, err := getExistingContainerInfo(ctx, containerID)
if err != nil {
return err
}
containerID = status.ID
kataLog = kataLog.WithFields(logrus.Fields{
"container": containerID,
"sandbox": sandboxID,
})
span.SetTag("container", containerID)
span.SetTag("sandbox", sandboxID)
setExternalLoggers(ctx, kataLog)
signum, err := processSignal(signal)
if err != nil {
return err
}
kataLog.WithField("signal", signal).WithField("container state", status.State.State).Info("kill")
// container MUST be created, running or paused
if status.State.State == types.StateReady || status.State.State == types.StateRunning || status.State.State == types.StatePaused {
if err := vci.KillContainer(ctx, sandboxID, containerID, signum, all); err != nil {
return err
}
} else if !all {
return fmt.Errorf("container not running")
}
if signum != syscall.SIGKILL && signum != syscall.SIGTERM {
return nil
}
containerType, err := oci.GetContainerType(status.Annotations)
if err != nil {
return err
}
switch containerType {
case vc.PodSandbox:
_, err = vci.StopSandbox(ctx, sandboxID, signum == syscall.SIGKILL)
case vc.PodContainer:
_, err = vci.StopContainer(ctx, sandboxID, containerID)
default:
return fmt.Errorf("Invalid container type found")
}
return err
}
func processSignal(signal string) (syscall.Signal, error) {
signum, signalOk := signalList[signal]
if signalOk {
return signum, nil
}
// Support for short name signals (INT)
signum, signalOk = signalList["SIG"+signal]
if signalOk {
return signum, nil
}
// Support for numeric signals
s, err := strconv.Atoi(signal)
if err != nil {
return 0, fmt.Errorf("Failed to convert signal %s to int", signal)
}
signum = syscall.Signal(s)
// Check whether signal is valid or not
for _, sig := range signalList {
if sig == signum {
// signal is a valid signal
return signum, nil
}
}
return 0, fmt.Errorf("Signal %s is not supported", signal)
}

View File

@ -1,431 +0,0 @@
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"flag"
"fmt"
"os"
"syscall"
"testing"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
vcAnnotations "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/vcmock"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
)
var (
testKillContainerFuncReturnNil = func(ctx context.Context, sandboxID, containerID string, signal syscall.Signal, all bool) error {
return nil
}
testStopContainerFuncReturnNil = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) {
return &vcmock.Container{}, nil
}
testStopSandboxFuncReturnNil = func(ctx context.Context, sandboxID string, force bool) (vc.VCSandbox, error) {
return &vcmock.Sandbox{}, nil
}
)
func TestProcessSignal(t *testing.T) {
tests := []struct {
signal string
valid bool
signum syscall.Signal
}{
{"SIGDCKBY", false, 0}, //invalid signal
{"DCKBY", false, 0}, //invalid signal
{"99999", false, 0}, //invalid signal
{"SIGTERM", true, syscall.SIGTERM},
{"TERM", true, syscall.SIGTERM},
{"15", true, syscall.SIGTERM},
}
for _, test := range tests {
signum, err := processSignal(test.signal)
if signum != test.signum {
t.Fatalf("signal received: %d expected signal: %d\n", signum, test.signum)
}
if test.valid && err != nil {
t.Fatalf("signal %s is a valid but a error was received: %s\n", test.signal, err)
}
if !test.valid && err == nil {
t.Fatalf("signal %s is not a valid signal and no error was reported\n", test.signal)
}
}
}
func testKillCLIFunctionTerminationSignalSuccessful(t *testing.T, sig string) {
assert := assert.New(t)
state := types.ContainerState{
State: types.StateRunning,
}
annotations := map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
}
testingImpl.KillContainerFunc = testKillContainerFuncReturnNil
testingImpl.StopContainerFunc = testStopContainerFuncReturnNil
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, annotations, &specs.Spec{}), nil
}
defer func() {
testingImpl.KillContainerFunc = nil
testingImpl.StopContainerFunc = nil
testingImpl.StatusContainerFunc = nil
}()
set := flag.NewFlagSet("", 0)
set.Parse([]string{testContainerID, sig})
execCLICommandFunc(assert, killCLICommand, set, false)
annotations = map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
}
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, annotations, &specs.Spec{}), nil
}
testingImpl.StopContainerFunc = nil
testingImpl.StopSandboxFunc = testStopSandboxFuncReturnNil
defer func() {
testingImpl.StopSandboxFunc = nil
}()
execCLICommandFunc(assert, killCLICommand, set, false)
}
func TestKillCLIFunctionSigkillSuccessful(t *testing.T) {
testKillCLIFunctionTerminationSignalSuccessful(t, "SIGKILL")
}
func TestKillCLIFunctionSigtermSuccessful(t *testing.T) {
testKillCLIFunctionTerminationSignalSuccessful(t, "SIGTERM")
}
func TestKillCLIFunctionNotTerminationSignalSuccessful(t *testing.T) {
assert := assert.New(t)
state := types.ContainerState{
State: types.StateRunning,
}
testingImpl.KillContainerFunc = testKillContainerFuncReturnNil
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil
}
defer func() {
testingImpl.KillContainerFunc = nil
testingImpl.StatusContainerFunc = nil
}()
set := flag.NewFlagSet("", 0)
set.Parse([]string{testContainerID, "SIGUSR1"})
execCLICommandFunc(assert, killCLICommand, set, false)
}
func TestKillCLIFunctionNoSignalSuccessful(t *testing.T) {
assert := assert.New(t)
state := types.ContainerState{
State: types.StateRunning,
}
annotations := map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
}
testingImpl.KillContainerFunc = testKillContainerFuncReturnNil
testingImpl.StopContainerFunc = testStopContainerFuncReturnNil
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, annotations, &specs.Spec{}), nil
}
defer func() {
testingImpl.KillContainerFunc = nil
testingImpl.StopContainerFunc = nil
testingImpl.StatusContainerFunc = nil
}()
set := flag.NewFlagSet("", 0)
set.Parse([]string{testContainerID})
execCLICommandFunc(assert, killCLICommand, set, false)
annotations = map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
}
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, annotations, &specs.Spec{}), nil
}
testingImpl.StopContainerFunc = nil
testingImpl.StopSandboxFunc = testStopSandboxFuncReturnNil
defer func() {
testingImpl.StopSandboxFunc = nil
}()
execCLICommandFunc(assert, killCLICommand, set, false)
}
func TestKillCLIFunctionEnableAllSuccessful(t *testing.T) {
assert := assert.New(t)
state := types.ContainerState{
State: types.StateRunning,
}
annotations := map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
}
testingImpl.KillContainerFunc = func(ctx context.Context, sandboxID, containerID string, signal syscall.Signal, all bool) error {
if !all {
return fmt.Errorf("Expecting -all flag = true, Got false")
}
return nil
}
testingImpl.StopContainerFunc = testStopContainerFuncReturnNil
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, annotations, &specs.Spec{}), nil
}
defer func() {
testingImpl.KillContainerFunc = nil
testingImpl.StopContainerFunc = nil
testingImpl.StatusContainerFunc = nil
}()
set := flag.NewFlagSet("", 0)
set.Bool("all", true, "")
set.Parse([]string{testContainerID})
execCLICommandFunc(assert, killCLICommand, set, false)
annotations = map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
}
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, annotations, &specs.Spec{}), nil
}
testingImpl.StopContainerFunc = nil
testingImpl.StopSandboxFunc = testStopSandboxFuncReturnNil
defer func() {
testingImpl.StopSandboxFunc = nil
}()
execCLICommandFunc(assert, killCLICommand, set, false)
}
func TestKillCLIFunctionNoContainerIDFailure(t *testing.T) {
assert := assert.New(t)
set := flag.NewFlagSet("", 0)
execCLICommandFunc(assert, killCLICommand, set, true)
}
func TestKillCLIFunctionContainerNotExistFailure(t *testing.T) {
assert := assert.New(t)
testingImpl.KillContainerFunc = testKillContainerFuncReturnNil
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{}, nil
}
defer func() {
testingImpl.KillContainerFunc = nil
}()
set := flag.NewFlagSet("", 0)
set.Parse([]string{testContainerID})
execCLICommandFunc(assert, killCLICommand, set, true)
}
func TestKillCLIFunctionInvalidSignalFailure(t *testing.T) {
assert := assert.New(t)
state := types.ContainerState{
State: types.StateRunning,
}
testingImpl.KillContainerFunc = testKillContainerFuncReturnNil
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil
}
defer func() {
testingImpl.KillContainerFunc = nil
testingImpl.StatusContainerFunc = nil
}()
set := flag.NewFlagSet("", 0)
set.Parse([]string{testContainerID, "SIGINVALID"})
execCLICommandFunc(assert, killCLICommand, set, true)
}
func TestKillCLIFunctionStatePausedSuccessful(t *testing.T) {
assert := assert.New(t)
state := types.ContainerState{
State: types.StatePaused,
}
testingImpl.KillContainerFunc = testKillContainerFuncReturnNil
testingImpl.StopContainerFunc = testStopContainerFuncReturnNil
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state,
map[string]string{string(vcAnnotations.ContainerTypeKey): string(vc.PodContainer)}, &specs.Spec{}), nil
}
defer func() {
testingImpl.KillContainerFunc = nil
testingImpl.StatusContainerFunc = nil
testingImpl.StopContainerFunc = nil
}()
set := flag.NewFlagSet("", 0)
set.Parse([]string{testContainerID})
execCLICommandFunc(assert, killCLICommand, set, false)
}
func TestKillCLIFunctionInvalidStateStoppedFailure(t *testing.T) {
assert := assert.New(t)
state := types.ContainerState{
State: types.StateStopped,
}
testingImpl.KillContainerFunc = testKillContainerFuncReturnNil
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil
}
defer func() {
testingImpl.KillContainerFunc = nil
testingImpl.StatusContainerFunc = nil
}()
set := flag.NewFlagSet("", 0)
set.Parse([]string{testContainerID})
execCLICommandFunc(assert, killCLICommand, set, true)
}
func TestKillCLIFunctionKillContainerFailure(t *testing.T) {
assert := assert.New(t)
state := types.ContainerState{
State: types.StateRunning,
}
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
set := flag.NewFlagSet("", 0)
set.Parse([]string{testContainerID})
execCLICommandFunc(assert, killCLICommand, set, true)
}
func TestKillCLIFunctionInvalidStateStoppedAllSuccess(t *testing.T) {
assert := assert.New(t)
state := types.ContainerState{
State: types.StateStopped,
}
testingImpl.KillContainerFunc = testKillContainerFuncReturnNil
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil
}
defer func() {
testingImpl.KillContainerFunc = nil
testingImpl.StatusContainerFunc = nil
}()
set := flag.NewFlagSet("", 0)
var all bool
set.BoolVar(&all, "all", false, "")
set.Parse([]string{"-all", testContainerID, "10"})
execCLICommandFunc(assert, killCLICommand, set, false)
}

View File

@ -1,394 +0,0 @@
// Copyright (c) 2014,2015,2016,2017 Docker, Inc.
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"strings"
"syscall"
"text/tabwriter"
"time"
"github.com/urfave/cli"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
oci "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/oci"
)
const formatOptions = `table or json`
// containerState represents the platform agnostic pieces relating to a
// running container's status and state
type containerState struct {
// Version is the OCI version for the container
Version string `json:"ociVersion"`
// ID is the container ID
ID string `json:"id"`
// InitProcessPid is the init process id in the parent namespace
InitProcessPid int `json:"pid"`
// Status is the current status of the container, running, paused, ...
Status string `json:"status"`
// Bundle is the path on the filesystem to the bundle
Bundle string `json:"bundle"`
// Rootfs is a path to a directory containing the container's root filesystem.
Rootfs string `json:"rootfs"`
// Created is the unix timestamp for the creation time of the container in UTC
Created time.Time `json:"created"`
// Annotations is the user defined annotations added to the config.
Annotations map[string]string `json:"annotations,omitempty"`
// The owner of the state directory (the owner of the container).
Owner string `json:"owner"`
}
type asset struct {
Path string `json:"path"`
Custom bool `json:"bool"`
}
// hypervisorDetails stores details of the hypervisor used to host
// the container
type hypervisorDetails struct {
HypervisorAsset asset `json:"hypervisorAsset"`
ImageAsset asset `json:"imageAsset"`
KernelAsset asset `json:"kernelAsset"`
}
// fullContainerState specifies the core state plus the hypervisor
// details
type fullContainerState struct {
containerState
CurrentHypervisorDetails hypervisorDetails `json:"currentHypervisor"`
LatestHypervisorDetails hypervisorDetails `json:"latestHypervisor"`
StaleAssets []string
}
type formatState interface {
Write(state []fullContainerState, showAll bool, file *os.File) error
}
type formatJSON struct{}
type formatIDList struct{}
type formatTabular struct{}
var listCLICommand = cli.Command{
Name: "list",
Usage: "lists containers started by " + name + " with the given root",
ArgsUsage: `
Where the given root is specified via the global option "--root"
(default: "` + defaultRootDirectory + `").
EXAMPLE 1:
To list containers created via the default "--root":
# ` + name + ` list
EXAMPLE 2:
To list containers created using a non-default value for "--root":
# ` + name + ` --root value list`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "format, f",
Value: "table",
Usage: `select one of: ` + formatOptions,
},
cli.BoolFlag{
Name: "quiet, q",
Usage: "display only container IDs",
},
cli.BoolFlag{
Name: "kata-all",
Usage: "display all available " + project + " information",
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
span, ctx := katautils.Trace(ctx, "list")
defer span.Finish()
s, err := getContainers(ctx, context)
if err != nil {
return err
}
file := defaultOutputFile
showAll := context.Bool("kata-all")
var fs formatState = formatIDList{}
if context.Bool("quiet") {
fs = formatIDList{}
} else {
switch context.String("format") {
case "table":
fs = formatTabular{}
case "json":
fs = formatJSON{}
default:
return fmt.Errorf("invalid format option")
}
}
return fs.Write(s, showAll, file)
},
}
// getStaleAssetsreturns compares the two specified hypervisorDetails objects
// and returns a list of strings representing which assets in "old" are not
// current compared to "new". If old and new are identical, the empty string
// will be returned.
//
// Notes:
//
// - This function is trivial because it relies upon the fact that new
// containers are always created with the latest versions of all assets.
//
// - WARNING: Since this function only compares local values, it is unable to
// determine if newer (remote) assets are available.
func getStaleAssets(old, new hypervisorDetails) []string {
var stale []string
if old.KernelAsset.Path != new.KernelAsset.Path {
if old.KernelAsset.Custom {
// The workload kernel asset is a custom one, i.e. it's not coming
// from the runtime configuration file. Thus it does not make sense
// to compare it against the configured kernel asset.
// We assume a custom kernel asset has been updated if the
// corresponding path no longer exists, i.e. it's been replaced by
// a new kernel, e.g. with a new version name.
// Replacing a custom kernel asset binary with exactly the same
// binary name won't allow us to detect if it's staled or not.
if _, err := os.Stat(old.KernelAsset.Path); os.IsNotExist(err) {
stale = append(stale, "kernel")
}
} else {
stale = append(stale, "kernel")
}
}
if old.ImageAsset.Path != new.ImageAsset.Path {
if old.ImageAsset.Custom {
// The workload image asset is a custom one, i.e. it's not coming
// from the runtime configuration file. Thus it does not make sense
// to compare it against the configured image asset.
// We assume a custom image asset has been updated if the
// corresponding path no longer exists, i.e. it's been replaced by
// a new image, e.g. with a new version name.
// Replacing a custom image asset binary with exactly the same
// binary name won't allow us to detect if it's staled or not.
if _, err := os.Stat(old.ImageAsset.Path); os.IsNotExist(err) {
stale = append(stale, "image")
}
} else {
stale = append(stale, "image")
}
}
return stale
}
func (f formatIDList) Write(state []fullContainerState, showAll bool, file *os.File) error {
for _, item := range state {
_, err := fmt.Fprintln(file, item.ID)
if err != nil {
return err
}
}
return nil
}
func (f formatTabular) Write(state []fullContainerState, showAll bool, file *os.File) error {
// values used by runc
flags := uint(0)
minWidth := 12
tabWidth := 1
padding := 3
w := tabwriter.NewWriter(file, minWidth, tabWidth, padding, ' ', flags)
fmt.Fprint(w, "ID\tPID\tSTATUS\tBUNDLE\tCREATED\tOWNER")
if showAll {
fmt.Fprint(w, "\tHYPERVISOR\tKERNEL\tIMAGE\tLATEST-KERNEL\tLATEST-IMAGE\tSTALE\n")
} else {
fmt.Fprintf(w, "\n")
}
for _, item := range state {
fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\t%s",
item.ID,
item.InitProcessPid,
item.Status,
item.Bundle,
item.Created.Format(time.RFC3339Nano),
item.Owner)
if showAll {
stale := strings.Join(item.StaleAssets, ",")
if stale == "" {
stale = "-"
}
current := item.CurrentHypervisorDetails
latest := item.LatestHypervisorDetails
all := fmt.Sprintf("\t%s\t%s\t%s",
current.HypervisorAsset.Path,
current.KernelAsset.Path,
current.ImageAsset.Path)
if !current.KernelAsset.Custom {
all += fmt.Sprintf("\t%s", latest.KernelAsset.Path)
} else {
all += fmt.Sprintf("\t%s", current.KernelAsset.Path)
}
if !current.ImageAsset.Custom {
all += fmt.Sprintf("\t%s", latest.ImageAsset.Path)
} else {
all += fmt.Sprintf("\t%s", current.ImageAsset.Path)
}
all += fmt.Sprintf("\t%s\n", stale)
fmt.Fprint(w, all)
} else {
fmt.Fprint(w, "\n")
}
}
return w.Flush()
}
func (f formatJSON) Write(state []fullContainerState, showAll bool, file *os.File) error {
return json.NewEncoder(file).Encode(state)
}
// getDirOwner returns the UID of the specified directory
func getDirOwner(dir string) (uint32, error) {
if dir == "" {
return 0, errors.New("BUG: need directory")
}
st, err := os.Stat(dir)
if err != nil {
return 0, err
}
if !st.IsDir() {
return 0, fmt.Errorf("%q is not a directory", dir)
}
statType, ok := st.Sys().(*syscall.Stat_t)
if !ok {
return 0, fmt.Errorf("cannot convert %+v to stat type for directory %q", st, dir)
}
return statType.Uid, nil
}
func getContainers(ctx context.Context, context *cli.Context) ([]fullContainerState, error) {
runtimeConfig, ok := context.App.Metadata["runtimeConfig"].(oci.RuntimeConfig)
if !ok {
return nil, errors.New("invalid runtime config")
}
latestHypervisorDetails := getHypervisorDetails(&runtimeConfig.HypervisorConfig)
sandboxList, err := vci.ListSandbox(ctx)
if err != nil {
return nil, err
}
var s []fullContainerState
for _, sandbox := range sandboxList {
if len(sandbox.ContainersStatus) == 0 {
// ignore empty sandboxes
continue
}
currentHypervisorDetails := getHypervisorDetails(&sandbox.HypervisorConfig)
for _, container := range sandbox.ContainersStatus {
ociState := oci.StatusToOCIState(container)
staleAssets := getStaleAssets(currentHypervisorDetails, latestHypervisorDetails)
uid, err := getDirOwner(container.RootFs)
if err != nil {
fmt.Fprintf(os.Stderr, "WARNING: failed to get container %s rootfs: %s\n", ociState.ID, err)
continue
}
owner := fmt.Sprintf("#%v", uid)
s = append(s, fullContainerState{
containerState: containerState{
Version: ociState.Version,
ID: ociState.ID,
InitProcessPid: ociState.Pid,
Status: ociState.Status,
Bundle: ociState.Bundle,
Rootfs: container.RootFs,
Created: container.StartTime,
Annotations: ociState.Annotations,
Owner: owner,
},
CurrentHypervisorDetails: currentHypervisorDetails,
LatestHypervisorDetails: latestHypervisorDetails,
StaleAssets: staleAssets,
})
}
}
return s, nil
}
// getHypervisorDetails returns details of the latest version of the
// hypervisor and the associated assets.
func getHypervisorDetails(hypervisorConfig *vc.HypervisorConfig) hypervisorDetails {
hypervisorPath, err := hypervisorConfig.HypervisorAssetPath()
if err != nil {
hypervisorPath = hypervisorConfig.HypervisorPath
}
kernelPath, err := hypervisorConfig.KernelAssetPath()
if err != nil {
kernelPath = hypervisorConfig.KernelPath
}
imagePath, err := hypervisorConfig.ImageAssetPath()
if err != nil {
imagePath = hypervisorConfig.ImagePath
}
return hypervisorDetails{
HypervisorAsset: asset{
Path: hypervisorPath,
Custom: hypervisorConfig.CustomHypervisorAsset(),
},
KernelAsset: asset{
Path: kernelPath,
Custom: hypervisorConfig.CustomKernelAsset(),
},
ImageAsset: asset{
Path: imagePath,
Custom: hypervisorConfig.CustomImageAsset(),
},
}
}

View File

@ -1,785 +0,0 @@
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"time"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
vcAnnotations "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/vcmock"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli"
)
type TestFileWriter struct {
Name string
File *os.File
}
var hypervisorDetails1 = hypervisorDetails{
HypervisorAsset: asset{
Path: "/hypervisor/path",
},
ImageAsset: asset{
Path: "/image/path",
},
KernelAsset: asset{
Path: "/kernel/path",
},
}
var hypervisorDetails2 = hypervisorDetails{
HypervisorAsset: asset{
Path: "/hypervisor/path2",
},
ImageAsset: asset{
Path: "/image/path2",
},
KernelAsset: asset{
Path: "/kernel/path2",
},
}
var hypervisorDetails3 = hypervisorDetails{
HypervisorAsset: asset{
Path: "/hypervisor/path3",
},
ImageAsset: asset{
Path: "/image/path3",
},
KernelAsset: asset{
Path: "/kernel/path3",
},
}
var testStatuses = []fullContainerState{
{
containerState: containerState{
Version: "",
ID: "1",
InitProcessPid: 1234,
Status: "running",
Bundle: "/somewhere/over/the/rainbow",
Created: time.Now().UTC(),
Annotations: map[string]string(nil),
Owner: "#0",
},
CurrentHypervisorDetails: hypervisorDetails1,
LatestHypervisorDetails: hypervisorDetails1,
StaleAssets: []string{},
},
{
containerState: containerState{
Version: "",
ID: "2",
InitProcessPid: 2345,
Status: "stopped",
Bundle: "/this/path/is/invalid",
Created: time.Now().UTC(),
Annotations: map[string]string(nil),
Owner: "#0",
},
CurrentHypervisorDetails: hypervisorDetails2,
LatestHypervisorDetails: hypervisorDetails2,
StaleAssets: []string{},
},
{
containerState: containerState{
Version: "",
ID: "3",
InitProcessPid: 9999,
Status: "ready",
Bundle: "/foo/bar/baz",
Created: time.Now().UTC(),
Annotations: map[string]string(nil),
Owner: "#0",
},
CurrentHypervisorDetails: hypervisorDetails3,
LatestHypervisorDetails: hypervisorDetails3,
StaleAssets: []string{},
},
}
// Implement the io.Writer interface
func (w *TestFileWriter) Write(bytes []byte) (n int, err error) {
return w.File.Write(bytes)
}
func formatListDataAsBytes(formatter formatState, state []fullContainerState, showAll bool) (bytes []byte, err error) {
tmpfile, err := ioutil.TempFile("", "formatListData-")
if err != nil {
return nil, err
}
defer os.Remove(tmpfile.Name())
err = formatter.Write(state, showAll, tmpfile)
if err != nil {
return nil, err
}
tmpfile.Close()
return ioutil.ReadFile(tmpfile.Name())
}
func formatListDataAsString(formatter formatState, state []fullContainerState, showAll bool) (lines []string, err error) {
bytes, err := formatListDataAsBytes(formatter, state, showAll)
if err != nil {
return nil, err
}
lines = strings.Split(string(bytes), "\n")
// Remove last line if empty
length := len(lines)
last := lines[length-1]
if last == "" {
lines = lines[:length-1]
}
return lines, nil
}
func TestStateToIDList(t *testing.T) {
// no header
expectedLength := len(testStatuses)
// showAll should not affect the output
for _, showAll := range []bool{true, false} {
lines, err := formatListDataAsString(&formatIDList{}, testStatuses, showAll)
if err != nil {
t.Fatal(err)
}
var expected []string
for _, s := range testStatuses {
expected = append(expected, s.ID)
}
length := len(lines)
if length != expectedLength {
t.Fatalf("Expected %d lines, got %d: %v", expectedLength, length, lines)
}
assert.Equal(t, lines, expected, "lines + expected")
}
}
func TestStateToTabular(t *testing.T) {
// +1 for header line
expectedLength := len(testStatuses) + 1
expectedDefaultHeaderPattern := `\AID\s+PID\s+STATUS\s+BUNDLE\s+CREATED\s+OWNER`
expectedExtendedHeaderPattern := `HYPERVISOR\s+KERNEL\s+IMAGE\s+LATEST-KERNEL\s+LATEST-IMAGE\s+STALE`
endingPattern := `\s*\z`
lines, err := formatListDataAsString(&formatTabular{}, testStatuses, false)
if err != nil {
t.Fatal(err)
}
length := len(lines)
expectedHeaderPattern := expectedDefaultHeaderPattern + endingPattern
expectedHeaderRE := regexp.MustCompile(expectedHeaderPattern)
if length != expectedLength {
t.Fatalf("Expected %d lines, got %d", expectedLength, length)
}
header := lines[0]
matches := expectedHeaderRE.FindAllStringSubmatch(header, -1)
if matches == nil {
t.Fatalf("Header line failed to match:\n"+
"pattern : %v\n"+
"line : %v\n",
expectedDefaultHeaderPattern,
header)
}
for i, status := range testStatuses {
lineIndex := i + 1
line := lines[lineIndex]
expectedLinePattern := fmt.Sprintf(`\A%s\s+%d\s+%s\s+%s\s+%s\s+%s\s*\z`,
regexp.QuoteMeta(status.ID),
status.InitProcessPid,
regexp.QuoteMeta(status.Status),
regexp.QuoteMeta(status.Bundle),
regexp.QuoteMeta(status.Created.Format(time.RFC3339Nano)),
regexp.QuoteMeta(status.Owner))
expectedLineRE := regexp.MustCompile(expectedLinePattern)
matches := expectedLineRE.FindAllStringSubmatch(line, -1)
if matches == nil {
t.Fatalf("Data line failed to match:\n"+
"pattern : %v\n"+
"line : %v\n",
expectedLinePattern,
line)
}
}
// Try again with full details this time
lines, err = formatListDataAsString(&formatTabular{}, testStatuses, true)
if err != nil {
t.Fatal(err)
}
length = len(lines)
expectedHeaderPattern = expectedDefaultHeaderPattern + `\s+` + expectedExtendedHeaderPattern + endingPattern
expectedHeaderRE = regexp.MustCompile(expectedHeaderPattern)
if length != expectedLength {
t.Fatalf("Expected %d lines, got %d", expectedLength, length)
}
header = lines[0]
matches = expectedHeaderRE.FindAllStringSubmatch(header, -1)
if matches == nil {
t.Fatalf("Header line failed to match:\n"+
"pattern : %v\n"+
"line : %v\n",
expectedDefaultHeaderPattern,
header)
}
for i, status := range testStatuses {
lineIndex := i + 1
line := lines[lineIndex]
expectedLinePattern := fmt.Sprintf(`\A%s\s+%d\s+%s\s+%s\s+%s\s+%s\s+%s\s+%s\s+%s\s+%s\s+%s\s+%s\s*\z`,
regexp.QuoteMeta(status.ID),
status.InitProcessPid,
regexp.QuoteMeta(status.Status),
regexp.QuoteMeta(status.Bundle),
regexp.QuoteMeta(status.Created.Format(time.RFC3339Nano)),
regexp.QuoteMeta(status.Owner),
regexp.QuoteMeta(status.CurrentHypervisorDetails.HypervisorAsset.Path),
regexp.QuoteMeta(status.CurrentHypervisorDetails.KernelAsset.Path),
regexp.QuoteMeta(status.CurrentHypervisorDetails.ImageAsset.Path),
regexp.QuoteMeta(status.LatestHypervisorDetails.KernelAsset.Path),
regexp.QuoteMeta(status.LatestHypervisorDetails.ImageAsset.Path),
regexp.QuoteMeta("-"))
expectedLineRE := regexp.MustCompile(expectedLinePattern)
matches := expectedLineRE.FindAllStringSubmatch(line, -1)
if matches == nil {
t.Fatalf("Data line failed to match:\n"+
"pattern : %v\n"+
"line : %v\n",
expectedLinePattern,
line)
}
}
}
func TestStateToJSON(t *testing.T) {
expectedLength := len(testStatuses)
// showAll should not affect the output
for _, showAll := range []bool{true, false} {
bytes, err := formatListDataAsBytes(&formatJSON{}, testStatuses, showAll)
if err != nil {
t.Fatal(err)
}
// Force capacity to match the original otherwise assert.Equal() complains.
states := make([]fullContainerState, 0, len(testStatuses))
err = json.Unmarshal(bytes, &states)
if err != nil {
t.Fatal(err)
}
length := len(states)
if length != expectedLength {
t.Fatalf("Expected %d lines, got %d", expectedLength, length)
}
// golang tip (what will presumably become v1.9) now
// stores a monotonic clock value as part of time.Time's
// internal representation (this is shown by a suffix in
// the form "m=±ddd.nnnnnnnnn" when calling String() on
// the time.Time object). However, this monotonic value
// is stripped out when marshaling.
//
// This behaviour change makes comparing the original
// object and the marshaled-and-then-unmarshaled copy of
// the object doomed to failure.
//
// The solution? Manually strip the monotonic time out
// of the original before comparison (yuck!)
//
// See:
//
// - https://go-review.googlesource.com/c/36255/7/src/time/time.go#54
//
for i := 0; i < expectedLength; i++ {
// remove monotonic time part
testStatuses[i].Created = testStatuses[i].Created.Truncate(0)
}
assert.Equal(t, states, testStatuses, "states + testStatuses")
}
}
func TestListCLIFunctionNoContainers(t *testing.T) {
ctx := createCLIContext(nil)
ctx.App.Name = "foo"
ctx.App.Metadata["foo"] = "bar"
fn, ok := listCLICommand.Action.(func(context *cli.Context) error)
assert.True(t, ok)
err := fn(ctx)
// no config in the Metadata
assert.Error(t, err)
}
func TestListGetContainersListSandboxFail(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir(testDir, "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
ctx := createCLIContext(nil)
ctx.App.Name = "foo"
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
ctx.App.Metadata = map[string]interface{}{
"runtimeConfig": runtimeConfig,
}
_, err = getContainers(context.Background(), ctx)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
}
func TestListGetContainers(t *testing.T) {
assert := assert.New(t)
testingImpl.ListSandboxFunc = func(ctx context.Context) ([]vc.SandboxStatus, error) {
// No pre-existing sandboxes
return []vc.SandboxStatus{}, nil
}
defer func() {
testingImpl.ListSandboxFunc = nil
}()
tmpdir, err := ioutil.TempDir(testDir, "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
ctx := createCLIContext(nil)
ctx.App.Name = "foo"
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
ctx.App.Metadata = map[string]interface{}{
"runtimeConfig": runtimeConfig,
}
state, err := getContainers(context.Background(), ctx)
assert.NoError(err)
assert.Equal(state, []fullContainerState(nil))
}
func TestListGetContainersSandboxWithoutContainers(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
testingImpl.ListSandboxFunc = func(ctx context.Context) ([]vc.SandboxStatus, error) {
return []vc.SandboxStatus{
{
ID: sandbox.ID(),
ContainersStatus: []vc.ContainerStatus(nil),
},
}, nil
}
defer func() {
testingImpl.ListSandboxFunc = nil
}()
tmpdir, err := ioutil.TempDir(testDir, "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
ctx := createCLIContext(nil)
ctx.App.Name = "foo"
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
ctx.App.Metadata = map[string]interface{}{
"runtimeConfig": runtimeConfig,
}
state, err := getContainers(context.Background(), ctx)
assert.NoError(err)
assert.Equal(state, []fullContainerState(nil))
}
func TestListGetContainersSandboxWithContainer(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir(testDir, "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
rootfs := filepath.Join(tmpdir, "rootfs")
err = os.MkdirAll(rootfs, testDirMode)
assert.NoError(err)
testingImpl.ListSandboxFunc = func(ctx context.Context) ([]vc.SandboxStatus, error) {
return []vc.SandboxStatus{
{
ID: sandbox.ID(),
ContainersStatus: []vc.ContainerStatus{
{
ID: sandbox.ID(),
Annotations: map[string]string{},
RootFs: rootfs,
},
},
},
}, nil
}
defer func() {
testingImpl.ListSandboxFunc = nil
}()
ctx := createCLIContext(nil)
ctx.App.Name = "foo"
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
ctx.App.Metadata["runtimeConfig"] = runtimeConfig
_, err = getContainers(context.Background(), ctx)
assert.NoError(err)
}
func TestListCLIFunctionFormatFail(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir(testDir, "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
quietFlags := flag.NewFlagSet("test", 0)
quietFlags.Bool("quiet", true, "")
tableFlags := flag.NewFlagSet("test", 0)
tableFlags.String("format", "table", "")
jsonFlags := flag.NewFlagSet("test", 0)
jsonFlags.String("format", "json", "")
invalidFlags := flag.NewFlagSet("test", 0)
invalidFlags.String("format", "not-a-valid-format", "")
type testData struct {
format string
flags *flag.FlagSet
}
data := []testData{
{"quiet", quietFlags},
{"table", tableFlags},
{"json", jsonFlags},
{"invalid", invalidFlags},
}
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
rootfs := filepath.Join(tmpdir, "rootfs")
testingImpl.ListSandboxFunc = func(ctx context.Context) ([]vc.SandboxStatus, error) {
return []vc.SandboxStatus{
{
ID: sandbox.ID(),
ContainersStatus: []vc.ContainerStatus{
{
ID: sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
},
RootFs: rootfs,
},
},
},
}, nil
}
defer func() {
testingImpl.ListSandboxFunc = nil
}()
savedOutputFile := defaultOutputFile
defer func() {
defaultOutputFile = savedOutputFile
}()
// purposely invalid
var invalidFile *os.File
for _, d := range data {
// start off with an invalid output file
defaultOutputFile = invalidFile
ctx := createCLIContext(d.flags)
ctx.App.Name = "foo"
ctx.App.Metadata["foo"] = "bar"
fn, ok := listCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok, d)
err = fn(ctx)
// no config in the Metadata
assert.Error(err, d)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err, d)
ctx.App.Metadata["runtimeConfig"] = runtimeConfig
err = os.MkdirAll(rootfs, testDirMode)
assert.NoError(err)
err = fn(ctx)
// invalid output file
assert.Error(err, d)
assert.False(vcmock.IsMockError(err), d)
output := filepath.Join(tmpdir, "output")
f, err := os.OpenFile(output, os.O_WRONLY|os.O_CREATE, testFileMode)
assert.NoError(err)
defer f.Close()
// output file is now valid
defaultOutputFile = f
err = fn(ctx)
if d.format == "invalid" {
assert.Error(err)
assert.False(vcmock.IsMockError(err), d)
} else {
assert.NoError(err)
}
}
}
func TestListCLIFunctionQuiet(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir(testDir, "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
rootfs := filepath.Join(tmpdir, "rootfs")
err = os.MkdirAll(rootfs, testDirMode)
assert.NoError(err)
testingImpl.ListSandboxFunc = func(ctx context.Context) ([]vc.SandboxStatus, error) {
return []vc.SandboxStatus{
{
ID: sandbox.ID(),
ContainersStatus: []vc.ContainerStatus{
{
ID: sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
},
RootFs: rootfs,
},
},
},
}, nil
}
defer func() {
testingImpl.ListSandboxFunc = nil
}()
set := flag.NewFlagSet("test", 0)
set.Bool("quiet", true, "")
ctx := createCLIContext(set)
ctx.App.Name = "foo"
ctx.App.Metadata["runtimeConfig"] = runtimeConfig
savedOutputFile := defaultOutputFile
defer func() {
defaultOutputFile = savedOutputFile
}()
output := filepath.Join(tmpdir, "output")
f, err := os.OpenFile(output, os.O_CREATE|os.O_WRONLY|os.O_SYNC, testFileMode)
assert.NoError(err)
defer f.Close()
defaultOutputFile = f
fn, ok := listCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
err = fn(ctx)
assert.NoError(err)
f.Close()
text, err := katautils.GetFileContents(output)
assert.NoError(err)
trimmed := strings.TrimSpace(text)
assert.Equal(testSandboxID, trimmed)
}
func TestListGetDirOwner(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir(testDir, "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
_, err = getDirOwner("")
// invalid parameter
assert.Error(err)
dir := filepath.Join(tmpdir, "dir")
_, err = getDirOwner(dir)
// ENOENT
assert.Error(err)
err = createEmptyFile(dir)
assert.NoError(err)
_, err = getDirOwner(dir)
// wrong file type
assert.Error(err)
err = os.Remove(dir)
assert.NoError(err)
err = os.MkdirAll(dir, testDirMode)
assert.NoError(err)
uid := uint32(os.Getuid())
dirUID, err := getDirOwner(dir)
assert.NoError(err)
assert.Equal(dirUID, uid)
}
func TestListWithRootfsMissShouldSuccess(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir(testDir, "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
rootfs := filepath.Join(tmpdir, "rootfs")
err = os.MkdirAll(rootfs, testDirMode)
assert.NoError(err)
testingImpl.ListSandboxFunc = func(ctx context.Context) ([]vc.SandboxStatus, error) {
return []vc.SandboxStatus{
{
ID: sandbox.ID(),
ContainersStatus: []vc.ContainerStatus{
{
ID: sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
},
RootFs: rootfs,
},
},
},
}, nil
}
defer func() {
testingImpl.ListSandboxFunc = nil
}()
set := flag.NewFlagSet("test", 0)
set.String("format", "table", "")
ctx := createCLIContext(set)
ctx.App.Name = "foo"
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
ctx.App.Metadata["runtimeConfig"] = runtimeConfig
fn, ok := listCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
err = fn(ctx)
assert.NoError(err)
// remove container rootfs, check list command should also work
assert.NoError(os.RemoveAll(rootfs))
err = fn(ctx)
assert.NoError(err)
}

View File

@ -120,27 +120,11 @@ var runtimeFlags = []cli.Flag{
// runtimeCommands is the list of supported command-line (sub-)
// commands.
var runtimeCommands = []cli.Command{
createCLICommand,
deleteCLICommand,
execCLICommand,
killCLICommand,
listCLICommand,
pauseCLICommand,
psCLICommand,
resumeCLICommand,
runCLICommand,
specCLICommand,
startCLICommand,
stateCLICommand,
updateCLICommand,
eventsCLICommand,
versionCLICommand,
// Kata Containers specific extensions
kataCheckCLICommand,
kataEnvCLICommand,
kataNetworkCLICommand,
kataOverheadCLICommand,
factoryCLICommand,
}

View File

@ -27,7 +27,6 @@ import (
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/compatoci"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/oci"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/rootless"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/vcmock"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
specs "github.com/opencontainers/runtime-spec/specs-go"
@ -47,6 +46,7 @@ const (
testSandboxID = "99999999-9999-9999-99999999999999999"
testContainerID = "1"
testBundle = "bundle"
testConsole = "/dev/pts/999"
)
var (
@ -1064,22 +1064,3 @@ func TestMainResetCLIGlobals(t *testing.T) {
assert.NotNil(cli.VersionPrinter)
assert.NotNil(savedCLIVersionPrinter)
}
func createTempContainerIDMapping(containerID, sandboxID string) (string, error) {
// Mocking rootless
rootless.IsRootless = func() bool { return false }
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
}
katautils.SetCtrsMapTreePath(ctrsMapTreePath)
return tmpDir, nil
}

View File

@ -1,229 +0,0 @@
// Copyright (c) 2018 Huawei Corporation.
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"encoding/json"
"fmt"
"os"
vcTypes "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/types"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
type networkType int
const (
// interfaceType for interface operation
interfaceType networkType = iota
routeType
)
var kataNetworkCLICommand = cli.Command{
Name: "kata-network",
Usage: "manage interfaces and routes for container",
Subcommands: []cli.Command{
addIfaceCommand,
delIfaceCommand,
listIfacesCommand,
updateRoutesCommand,
listRoutesCommand,
},
Action: func(context *cli.Context) error {
return cli.ShowSubcommandHelp(context)
},
}
var addIfaceCommand = cli.Command{
Name: "add-iface",
Usage: "add an interface to a container",
ArgsUsage: `add-iface <container-id> file or - for stdin`,
Flags: []cli.Flag{},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
return networkModifyCommand(ctx, context.Args().First(), context.Args().Get(1), interfaceType, true)
},
}
var delIfaceCommand = cli.Command{
Name: "del-iface",
Usage: "delete an interface from a container",
ArgsUsage: `del-iface <container-id> file or - for stdin`,
Flags: []cli.Flag{},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
return networkModifyCommand(ctx, context.Args().First(), context.Args().Get(1), interfaceType, false)
},
}
var listIfacesCommand = cli.Command{
Name: "list-ifaces",
Usage: "list network interfaces in a container",
ArgsUsage: `list-ifaces <container-id>`,
Flags: []cli.Flag{},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
return networkListCommand(ctx, context.Args().First(), interfaceType)
},
}
var updateRoutesCommand = cli.Command{
Name: "update-routes",
Usage: "update routes of a container",
ArgsUsage: `update-routes <container-id> file or - for stdin`,
Flags: []cli.Flag{},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
return networkModifyCommand(ctx, context.Args().First(), context.Args().Get(1), routeType, true)
},
}
var listRoutesCommand = cli.Command{
Name: "list-routes",
Usage: "list network routes in a container",
ArgsUsage: `list-routes <container-id>`,
Flags: []cli.Flag{},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
return networkListCommand(ctx, context.Args().First(), routeType)
},
}
func networkModifyCommand(ctx context.Context, containerID, input string, opType networkType, add bool) (err error) {
status, sandboxID, err := getExistingContainerInfo(ctx, containerID)
if err != nil {
return err
}
containerID = status.ID
kataLog = kataLog.WithFields(logrus.Fields{
"container": containerID,
"sandbox": sandboxID,
})
setExternalLoggers(ctx, kataLog)
// container MUST be running
if status.State.State != types.StateRunning {
return fmt.Errorf("container %s is not running", containerID)
}
var (
f *os.File
output = defaultOutputFile
)
if input == "-" {
f = os.Stdin
} else {
f, err = os.Open(input)
if err != nil {
return err
}
defer f.Close()
}
switch opType {
case interfaceType:
var inf, resultingInf *vcTypes.Interface
if err = json.NewDecoder(f).Decode(&inf); err != nil {
return err
}
if add {
resultingInf, err = vci.AddInterface(ctx, sandboxID, inf)
if err != nil {
kataLog.WithField("resulting-interface", fmt.Sprintf("%+v", resultingInf)).
WithError(err).Error("add interface failed")
}
} else {
resultingInf, err = vci.RemoveInterface(ctx, sandboxID, inf)
if err != nil {
kataLog.WithField("resulting-interface", fmt.Sprintf("%+v", resultingInf)).
WithError(err).Error("delete interface failed")
}
}
json.NewEncoder(output).Encode(resultingInf)
case routeType:
var routes, resultingRoutes []*vcTypes.Route
if err = json.NewDecoder(f).Decode(&routes); err != nil {
return err
}
resultingRoutes, err = vci.UpdateRoutes(ctx, sandboxID, routes)
json.NewEncoder(output).Encode(resultingRoutes)
if err != nil {
kataLog.WithField("resulting-routes", fmt.Sprintf("%+v", resultingRoutes)).
WithError(err).Error("update routes failed")
}
}
return err
}
func networkListCommand(ctx context.Context, containerID string, opType networkType) (err error) {
status, sandboxID, err := getExistingContainerInfo(ctx, containerID)
if err != nil {
return err
}
containerID = status.ID
kataLog = kataLog.WithFields(logrus.Fields{
"container": containerID,
"sandbox": sandboxID,
})
setExternalLoggers(ctx, kataLog)
// container MUST be running
if status.State.State != types.StateRunning {
return fmt.Errorf("container %s is not running", containerID)
}
var file = defaultOutputFile
switch opType {
case interfaceType:
var interfaces []*vcTypes.Interface
interfaces, err = vci.ListInterfaces(ctx, sandboxID)
if err != nil {
kataLog.WithField("existing-interfaces", fmt.Sprintf("%+v", interfaces)).
WithError(err).Error("list interfaces failed")
}
json.NewEncoder(file).Encode(interfaces)
case routeType:
var routes []*vcTypes.Route
routes, err = vci.ListRoutes(ctx, sandboxID)
if err != nil {
kataLog.WithField("resulting-routes", fmt.Sprintf("%+v", routes)).
WithError(err).Error("update routes failed")
}
json.NewEncoder(file).Encode(routes)
}
return err
}

View File

@ -1,92 +0,0 @@
// Copyright (c) 2018 Huawei Corporation.
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"flag"
"io/ioutil"
"os"
"testing"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
vcTypes "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/types"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
)
var (
testAddInterfaceFuncReturnNil = func(ctx context.Context, sandboxID string, inf *vcTypes.Interface) (*vcTypes.Interface, error) {
return nil, nil
}
testRemoveInterfaceFuncReturnNil = func(ctx context.Context, sandboxID string, inf *vcTypes.Interface) (*vcTypes.Interface, error) {
return nil, nil
}
testListInterfacesFuncReturnNil = func(ctx context.Context, sandboxID string) ([]*vcTypes.Interface, error) {
return nil, nil
}
testUpdateRoutsFuncReturnNil = func(ctx context.Context, sandboxID string, routes []*vcTypes.Route) ([]*vcTypes.Route, error) {
return nil, nil
}
testListRoutesFuncReturnNil = func(ctx context.Context, sandboxID string) ([]*vcTypes.Route, error) {
return nil, nil
}
)
func TestNetworkCliFunction(t *testing.T) {
assert := assert.New(t)
state := types.ContainerState{
State: types.StateRunning,
}
testingImpl.AddInterfaceFunc = testAddInterfaceFuncReturnNil
testingImpl.RemoveInterfaceFunc = testRemoveInterfaceFuncReturnNil
testingImpl.ListInterfacesFunc = testListInterfacesFuncReturnNil
testingImpl.UpdateRoutesFunc = testUpdateRoutsFuncReturnNil
testingImpl.ListRoutesFunc = testListRoutesFuncReturnNil
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil
}
defer func() {
testingImpl.AddInterfaceFunc = nil
testingImpl.RemoveInterfaceFunc = nil
testingImpl.ListInterfacesFunc = nil
testingImpl.UpdateRoutesFunc = nil
testingImpl.ListRoutesFunc = nil
testingImpl.StatusContainerFunc = nil
}()
set := flag.NewFlagSet("", 0)
execCLICommandFunc(assert, addIfaceCommand, set, true)
set.Parse([]string{testContainerID})
execCLICommandFunc(assert, listIfacesCommand, set, false)
execCLICommandFunc(assert, listRoutesCommand, set, false)
f, err := ioutil.TempFile("", "interface")
defer os.Remove(f.Name())
assert.NoError(err)
assert.NotNil(f)
f.WriteString("{}")
set.Parse([]string{testContainerID, f.Name()})
execCLICommandFunc(assert, addIfaceCommand, set, false)
execCLICommandFunc(assert, delIfaceCommand, set, false)
f.Seek(0, 0)
f.WriteString("[{}]")
f.Close()
execCLICommandFunc(assert, updateRoutesCommand, set, false)
}

View File

@ -1,213 +0,0 @@
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"bufio"
"context"
"fmt"
"net"
"os"
"path/filepath"
"strings"
"syscall"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
"github.com/opencontainers/runc/libcontainer/utils"
)
// Contants related to cgroup memory directory
const (
// Filesystem type corresponding to CGROUP_SUPER_MAGIC as listed
// here: http://man7.org/linux/man-pages/man2/statfs.2.html
cgroupFsType = 0x27e0eb
)
var cgroupsDirPath string
var procMountInfo = "/proc/self/mountinfo"
// getContainerInfo returns the container status and its sandbox ID.
func getContainerInfo(ctx context.Context, containerID string) (vc.ContainerStatus, string, error) {
// container ID MUST be provided.
if containerID == "" {
return vc.ContainerStatus{}, "", fmt.Errorf("Missing container ID")
}
sandboxID, err := katautils.FetchContainerIDMapping(containerID)
if err != nil {
return vc.ContainerStatus{}, "", err
}
if sandboxID == "" {
// Not finding a container should not trigger an error as
// getContainerInfo is used for checking the existence and
// the absence of a container ID.
return vc.ContainerStatus{}, "", nil
}
ctrStatus, err := vci.StatusContainer(ctx, sandboxID, containerID)
if err != nil {
return vc.ContainerStatus{}, "", err
}
return ctrStatus, sandboxID, nil
}
func getExistingContainerInfo(ctx context.Context, containerID string) (vc.ContainerStatus, string, error) {
cStatus, sandboxID, err := getContainerInfo(ctx, containerID)
if err != nil {
return vc.ContainerStatus{}, "", err
}
// container ID MUST exist.
if cStatus.ID == "" {
return vc.ContainerStatus{}, "", fmt.Errorf("Container ID (%v) does not exist", containerID)
}
return cStatus, sandboxID, nil
}
func validCreateParams(ctx context.Context, containerID, bundlePath string) (string, error) {
// container ID MUST be provided.
if containerID == "" {
return "", fmt.Errorf("Missing container ID")
}
// container ID MUST be unique.
cStatus, _, err := getContainerInfo(ctx, containerID)
if err != nil {
return "", err
}
if cStatus.ID != "" {
return "", fmt.Errorf("ID already in use, unique ID should be provided")
}
// bundle path MUST be provided.
if bundlePath == "" {
return "", fmt.Errorf("Missing bundle path")
}
// bundle path MUST be valid.
fileInfo, err := os.Stat(bundlePath)
if err != nil {
return "", fmt.Errorf("Invalid bundle path '%s': %s", bundlePath, err)
}
if !fileInfo.IsDir() {
return "", fmt.Errorf("Invalid bundle path '%s', it should be a directory", bundlePath)
}
resolved, err := katautils.ResolvePath(bundlePath)
if err != nil {
return "", err
}
return resolved, nil
}
func isCgroupMounted(cgroupPath string) bool {
var statFs syscall.Statfs_t
if err := syscall.Statfs(cgroupPath, &statFs); err != nil {
return false
}
if statFs.Type != archConvertStatFs(cgroupFsType) {
return false
}
return true
}
func setupConsole(consolePath, consoleSockPath string) (string, error) {
if consolePath != "" {
return consolePath, nil
}
if consoleSockPath == "" {
return "", nil
}
console, err := newConsole()
if err != nil {
return "", err
}
defer console.master.Close()
// Open the socket path provided by the caller
conn, err := net.Dial("unix", consoleSockPath)
if err != nil {
return "", err
}
uConn, ok := conn.(*net.UnixConn)
if !ok {
return "", fmt.Errorf("casting to *net.UnixConn failed")
}
socket, err := uConn.File()
if err != nil {
return "", err
}
// Send the parent fd through the provided socket
if err := utils.SendFd(socket, console.master.Name(), console.master.Fd()); err != nil {
return "", err
}
return console.slavePath, nil
}
func noNeedForOutput(detach bool, tty bool) bool {
if !detach {
return false
}
if !tty {
return false
}
return true
}
func getCgroupsDirPath(mountInfoFile string) (string, error) {
if cgroupsDirPath != "" {
return cgroupsDirPath, nil
}
f, err := os.Open(mountInfoFile)
if err != nil {
return "", err
}
defer f.Close()
var cgroupRootPath string
scanner := bufio.NewScanner(f)
for scanner.Scan() {
text := scanner.Text()
index := strings.Index(text, " - ")
if index < 0 {
continue
}
fields := strings.Split(text, " ")
postSeparatorFields := strings.Fields(text[index+3:])
numPostFields := len(postSeparatorFields)
if len(fields) < 5 || postSeparatorFields[0] != "cgroup" || numPostFields < 3 {
continue
}
cgroupRootPath = filepath.Dir(fields[4])
break
}
if _, err = os.Stat(cgroupRootPath); err != nil {
return "", err
}
return cgroupRootPath, nil
}

View File

@ -1,319 +0,0 @@
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"fmt"
"io/ioutil"
"math/rand"
"net"
"os"
"path/filepath"
"testing"
"time"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
vcAnnotations "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/vcmock"
"github.com/opencontainers/runc/libcontainer/utils"
"github.com/stretchr/testify/assert"
)
var (
consolePathTest = "console-test"
consoleSocketPathTest = "console-socket-test"
)
func TestGetContainerInfoContainerIDEmptyFailure(t *testing.T) {
assert := assert.New(t)
status, _, err := getContainerInfo(context.Background(), "")
assert.Error(err, "This test should fail because containerID is empty")
assert.Empty(status.ID, "Expected blank fullID, but got %v", status.ID)
}
func TestGetContainerInfo(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
containerID := testContainerID
containerStatus := vc.ContainerStatus{
ID: containerID,
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
},
}
path, err := createTempContainerIDMapping(containerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return containerStatus, nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
status, sandboxID, err := getContainerInfo(context.Background(), testContainerID)
assert.NoError(err)
assert.Equal(sandboxID, sandbox.ID())
assert.Equal(status, containerStatus)
}
func TestValidCreateParamsContainerIDEmptyFailure(t *testing.T) {
assert := assert.New(t)
_, err := validCreateParams(context.Background(), "", "")
assert.Error(err, "This test should fail because containerID is empty")
assert.False(vcmock.IsMockError(err))
}
func TestGetExistingContainerInfoContainerIDEmptyFailure(t *testing.T) {
assert := assert.New(t)
status, _, err := getExistingContainerInfo(context.Background(), "")
assert.Error(err, "This test should fail because containerID is empty")
assert.Empty(status.ID, "Expected blank fullID, but got %v", status.ID)
}
func TestValidCreateParamsContainerIDNotUnique(t *testing.T) {
assert := assert.New(t)
testSandboxID2 := testSandboxID + "2"
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
err = os.MkdirAll(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID2), 0750)
assert.NoError(err)
_, err = validCreateParams(context.Background(), testContainerID, "")
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
func TestValidCreateParamsInvalidBundle(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
bundlePath := filepath.Join(tmpdir, "bundle")
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
_, err = validCreateParams(context.Background(), testContainerID, bundlePath)
// bundle is ENOENT
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
func TestValidCreateParamsBundleIsAFile(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
bundlePath := filepath.Join(tmpdir, "bundle")
err = createEmptyFile(bundlePath)
assert.NoError(err)
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
_, err = validCreateParams(context.Background(), testContainerID, bundlePath)
// bundle exists as a file, not a directory
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
func TestSetupConsoleExistingConsolePathSuccessful(t *testing.T) {
assert := assert.New(t)
console, err := setupConsole(consolePathTest, "")
assert.NoError(err)
assert.Equal(console, consolePathTest, "Got %q, Expecting %q", console, consolePathTest)
}
func TestSetupConsoleExistingConsolePathAndConsoleSocketPathSuccessful(t *testing.T) {
assert := assert.New(t)
console, err := setupConsole(consolePathTest, consoleSocketPathTest)
assert.NoError(err)
assert.Equal(console, consolePathTest, "Got %q, Expecting %q", console, consolePathTest)
}
func TestSetupConsoleEmptyPathsSuccessful(t *testing.T) {
assert := assert.New(t)
console, err := setupConsole("", "")
assert.NoError(err)
assert.Empty(console, "Console path should be empty, got %q instead", console)
}
func TestSetupConsoleExistingConsoleSocketPath(t *testing.T) {
assert := assert.New(t)
dir, err := ioutil.TempDir("", "test-socket")
assert.NoError(err)
defer os.RemoveAll(dir)
sockName := filepath.Join(dir, "console.sock")
l, err := net.Listen("unix", sockName)
assert.NoError(err)
console, err := setupConsole("", sockName)
assert.NoError(err)
waitCh := make(chan error)
go func() {
conn, err1 := l.Accept()
if err != nil {
waitCh <- err1
}
uConn, ok := conn.(*net.UnixConn)
if !ok {
waitCh <- fmt.Errorf("casting to *net.UnixConn failed")
}
f, err1 := uConn.File()
if err != nil {
waitCh <- err1
}
_, err1 = utils.RecvFd(f)
waitCh <- err1
}()
assert.NotEmpty(console, "Console socket path should not be empty")
err = <-waitCh
assert.NoError(err)
}
func TestSetupConsoleNotExistingSocketPathFailure(t *testing.T) {
assert := assert.New(t)
console, err := setupConsole("", "unknown-sock-path")
assert.Error(err, "This test should fail because the console socket path does not exist")
assert.Empty(console, "This test should fail because the console socket path does not exist")
}
func testNoNeedForOutput(t *testing.T, detach bool, tty bool, expected bool) {
assert := assert.New(t)
result := noNeedForOutput(detach, tty)
assert.Equal(result, expected)
}
func TestNoNeedForOutputDetachTrueTtyTrue(t *testing.T) {
testNoNeedForOutput(t, true, true, true)
}
func TestNoNeedForOutputDetachFalseTtyTrue(t *testing.T) {
testNoNeedForOutput(t, false, true, false)
}
func TestNoNeedForOutputDetachFalseTtyFalse(t *testing.T) {
testNoNeedForOutput(t, false, false, false)
}
func TestNoNeedForOutputDetachTrueTtyFalse(t *testing.T) {
testNoNeedForOutput(t, true, false, false)
}
func TestIsCgroupMounted(t *testing.T) {
assert := assert.New(t)
r := rand.New(rand.NewSource(time.Now().Unix()))
randPath := fmt.Sprintf("/path/to/random/%d", r.Int63())
assert.False(isCgroupMounted(randPath), "%s does not exist", randPath)
assert.False(isCgroupMounted(os.TempDir()), "%s is not a cgroup", os.TempDir())
cgroupsDirPath = ""
cgroupRootPath, err := getCgroupsDirPath(procMountInfo)
if err != nil {
assert.NoError(err)
}
memoryCgroupPath := filepath.Join(cgroupRootPath, "memory")
if _, err := os.Stat(memoryCgroupPath); os.IsNotExist(err) {
t.Skipf("memory cgroup does not exist: %s", memoryCgroupPath)
}
assert.True(isCgroupMounted(memoryCgroupPath), "%s is a cgroup", memoryCgroupPath)
}
func TestGetCgroupsDirPath(t *testing.T) {
assert := assert.New(t)
type testData struct {
contents string
expectedResult string
expectError bool
}
dir, err := ioutil.TempDir("", "")
if err != nil {
assert.NoError(err)
}
defer os.RemoveAll(dir)
// make sure tested cgroupsDirPath is existed
testedCgroupDir := filepath.Join(dir, "weirdCgroup")
err = os.Mkdir(testedCgroupDir, testDirMode)
assert.NoError(err)
weirdCgroupPath := filepath.Join(testedCgroupDir, "memory")
data := []testData{
{fmt.Sprintf("num1 num2 num3 / %s num6 num7 - cgroup cgroup rw,memory", weirdCgroupPath), testedCgroupDir, false},
// cgroup mount is not properly formated, if fields post - less than 3
{fmt.Sprintf("num1 num2 num3 / %s num6 num7 - cgroup cgroup ", weirdCgroupPath), "", true},
{"a a a a a a a - b c d", "", true},
{"a \na b \na b c\na b c d", "", true},
{"", "", true},
}
file := filepath.Join(dir, "mountinfo")
//file does not exist, should error here
_, err = getCgroupsDirPath(file)
assert.Error(err)
for _, d := range data {
err := ioutil.WriteFile(file, []byte(d.contents), testFileMode)
assert.NoError(err)
cgroupsDirPath = ""
path, err := getCgroupsDirPath(file)
if d.expectError {
assert.Error(err, fmt.Sprintf("got %q, test data: %+v", path, d))
} else {
assert.NoError(err, fmt.Sprintf("got %q, test data: %+v", path, d))
}
assert.Equal(d.expectedResult, path)
}
}

View File

@ -1,93 +0,0 @@
// Copyright (c) 2014,2015,2016 Docker, Inc.
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var noteText = `Use "` + name + ` list" to identify container statuses.`
var pauseCLICommand = cli.Command{
Name: "pause",
Usage: "suspend all processes in a container",
ArgsUsage: `<container-id>
Where "<container-id>" is the container name to be paused.`,
Description: `The pause command suspends all processes in a container.
` + noteText,
Action: pause,
}
var resumeCLICommand = cli.Command{
Name: "resume",
Usage: "unpause all previously paused processes in a container",
ArgsUsage: `<container-id>
Where "<container-id>" is the container name to be resumed.`,
Description: `The resume command unpauses all processes in a container.
` + noteText,
Action: resume,
}
func pause(c *cli.Context) error {
return toggle(c, true)
}
func resume(c *cli.Context) error {
return toggle(c, false)
}
func toggle(c *cli.Context, pause bool) error {
ctx, err := cliContextToContext(c)
if err != nil {
return err
}
return toggleContainerPause(ctx, c.Args().First(), pause)
}
func toggleContainerPause(ctx context.Context, containerID string, pause bool) (err error) {
span, _ := katautils.Trace(ctx, "pause")
defer span.Finish()
span.SetTag("pause", pause)
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(ctx, kataLog)
span.SetTag("container", containerID)
// Checks the MUST and MUST NOT from OCI runtime specification
status, sandboxID, err := getExistingContainerInfo(ctx, containerID)
if err != nil {
return err
}
containerID = status.ID
kataLog = kataLog.WithFields(logrus.Fields{
"container": containerID,
"sandbox": sandboxID,
})
setExternalLoggers(ctx, kataLog)
span.SetTag("container", containerID)
span.SetTag("sandbox", sandboxID)
if pause {
err = vci.PauseContainer(ctx, sandboxID, containerID)
} else {
err = vci.ResumeContainer(ctx, sandboxID, containerID)
}
return err
}

View File

@ -1,175 +0,0 @@
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"flag"
"io/ioutil"
"os"
"testing"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
)
var (
testPauseContainerFuncReturnNil = func(ctx context.Context, sandboxID, containerID string) error {
return nil
}
testResumeContainerFuncReturnNil = func(ctx context.Context, sandboxID, containerID string) error {
return nil
}
)
func TestPauseCLIFunctionSuccessful(t *testing.T) {
assert := assert.New(t)
state := types.ContainerState{
State: types.StateRunning,
}
testingImpl.PauseContainerFunc = testPauseContainerFuncReturnNil
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil
}
defer func() {
testingImpl.PauseContainerFunc = nil
testingImpl.StatusContainerFunc = nil
}()
set := flag.NewFlagSet("", 0)
set.Parse([]string{testContainerID})
execCLICommandFunc(assert, pauseCLICommand, set, false)
}
func TestPauseCLIFunctionContainerNotExistFailure(t *testing.T) {
assert := assert.New(t)
testingImpl.PauseContainerFunc = testPauseContainerFuncReturnNil
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
defer func() {
testingImpl.PauseContainerFunc = nil
}()
set := flag.NewFlagSet("", 0)
set.Parse([]string{testContainerID})
execCLICommandFunc(assert, pauseCLICommand, set, true)
}
func TestPauseCLIFunctionPauseContainerFailure(t *testing.T) {
assert := assert.New(t)
state := types.ContainerState{
State: types.StateRunning,
}
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
set := flag.NewFlagSet("", 0)
set.Parse([]string{testContainerID})
execCLICommandFunc(assert, pauseCLICommand, set, true)
}
func TestResumeCLIFunctionSuccessful(t *testing.T) {
assert := assert.New(t)
state := types.ContainerState{
State: types.StateRunning,
}
testingImpl.ResumeContainerFunc = testResumeContainerFuncReturnNil
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil
}
defer func() {
testingImpl.ResumeContainerFunc = nil
testingImpl.StatusContainerFunc = nil
}()
set := flag.NewFlagSet("", 0)
set.Parse([]string{testContainerID})
execCLICommandFunc(assert, resumeCLICommand, set, false)
}
func TestResumeCLIFunctionContainerNotExistFailure(t *testing.T) {
assert := assert.New(t)
testingImpl.ResumeContainerFunc = testResumeContainerFuncReturnNil
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
defer func() {
testingImpl.ResumeContainerFunc = nil
}()
set := flag.NewFlagSet("", 0)
set.Parse([]string{testContainerID})
execCLICommandFunc(assert, resumeCLICommand, set, true)
}
func TestResumeCLIFunctionPauseContainerFailure(t *testing.T) {
assert := assert.New(t)
state := types.ContainerState{
State: types.StateRunning,
}
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
set := flag.NewFlagSet("", 0)
set.Parse([]string{testContainerID})
execCLICommandFunc(assert, resumeCLICommand, set, true)
}

View File

@ -1,106 +0,0 @@
// Copyright (c) 2014,2015,2016 Docker, Inc.
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"fmt"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var psCLICommand = cli.Command{
Name: "ps",
Usage: "ps displays the processes running inside a container",
ArgsUsage: `<container-id> [ps options]`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "format, f",
Value: "table",
Usage: `select one of: ` + formatOptions,
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
if !context.Args().Present() {
return fmt.Errorf("Missing container ID, should at least provide one")
}
var args []string
if len(context.Args()) > 1 {
// [1:] is to remove container_id:
// context.Args(): [container_id ps_arg1 ps_arg2 ...]
// args: [ps_arg1 ps_arg2 ...]
args = context.Args()[1:]
}
return ps(ctx, context.Args().First(), context.String("format"), args)
},
SkipArgReorder: true,
}
func ps(ctx context.Context, containerID, format string, args []string) error {
span, _ := katautils.Trace(ctx, "ps")
defer span.Finish()
if containerID == "" {
return fmt.Errorf("Missing container ID")
}
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(ctx, kataLog)
span.SetTag("container", containerID)
// Checks the MUST and MUST NOT from OCI runtime specification
status, sandboxID, err := getExistingContainerInfo(ctx, containerID)
if err != nil {
return err
}
containerID = status.ID
kataLog = kataLog.WithFields(logrus.Fields{
"container": containerID,
"sandbox": sandboxID,
})
setExternalLoggers(ctx, kataLog)
span.SetTag("container", containerID)
span.SetTag("sandbox", sandboxID)
// container MUST be running
if status.State.State != types.StateRunning {
return fmt.Errorf("Container %s is not running", containerID)
}
var options vc.ProcessListOptions
options.Args = args
if len(options.Args) == 0 {
options.Args = []string{"-ef"}
}
options.Format = format
msg, err := vci.ProcessListContainer(ctx, sandboxID, containerID, options)
if err != nil {
return err
}
fmt.Print(string(msg))
return nil
}

View File

@ -1,120 +0,0 @@
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"flag"
"os"
"testing"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
vcAnnotations "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/vcmock"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli"
)
func TestPSCLIAction(t *testing.T) {
assert := assert.New(t)
flagSet := flag.NewFlagSet("flag", flag.ContinueOnError)
flagSet.Parse([]string{"runtime"})
// create a new fake context
ctx := createCLIContext(flagSet)
// get Action function
actionFunc, ok := psCLICommand.Action.(func(ctx *cli.Context) error)
assert.True(ok)
err := actionFunc(ctx)
assert.Error(err, "Missing container ID")
}
func TestPSFailure(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testContainerID,
}
sandbox.MockContainers = []*vcmock.Container{
{
MockID: sandbox.ID(),
MockSandbox: sandbox,
},
}
path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID())
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
},
}, nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
// inexistent container
err = ps(context.Background(), "xyz123abc", "json", []string{"-ef"})
assert.Error(err)
// container is not running
err = ps(context.Background(), sandbox.ID(), "json", []string{"-ef"})
assert.Error(err)
}
func TestPSSuccessful(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testContainerID,
}
sandbox.MockContainers = []*vcmock.Container{
{
MockID: sandbox.ID(),
MockSandbox: sandbox,
},
}
path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID())
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
State: types.ContainerState{
State: types.StateRunning,
},
ID: sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
},
}, nil
}
testingImpl.ProcessListContainerFunc = func(ctx context.Context, sandboxID, containerID string, options vc.ProcessListOptions) (vc.ProcessList, error) {
return []byte("echo,sleep,grep"), nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
testingImpl.ProcessListContainerFunc = nil
}()
err = ps(context.Background(), sandbox.ID(), "json", []string{})
assert.NoError(err)
}

View File

@ -1,129 +0,0 @@
// Copyright (c) 2014,2015,2016 Docker, Inc.
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"errors"
"fmt"
"os"
"syscall"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/oci"
"github.com/urfave/cli"
)
var runCLICommand = cli.Command{
Name: "run",
Usage: "create and run a container",
ArgsUsage: `<container-id>
<container-id> is your name for the instance of the container that you
are starting. The name you provide for the container instance must be unique
on your host.`,
Description: `The run command creates an instance of a container for a bundle. The bundle
is a directory with a specification file named "config.json" and a root
filesystem.`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "bundle, b",
Value: "",
Usage: `path to the root of the bundle directory, defaults to the current directory`,
},
cli.StringFlag{
Name: "console",
Value: "",
Usage: "path to a pseudo terminal",
},
cli.StringFlag{
Name: "console-socket",
Value: "",
Usage: "path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal",
},
cli.StringFlag{
Name: "pid-file",
Value: "",
Usage: "specify the file to write the process id to",
},
cli.BoolFlag{
Name: "detach, d",
Usage: "detach from the container's process",
},
cli.BoolFlag{
Name: "no-pivot",
Usage: "warning: this flag is meaningless to kata-runtime, just defined in order to be compatible with docker in ramdisk",
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
runtimeConfig, ok := context.App.Metadata["runtimeConfig"].(oci.RuntimeConfig)
if !ok {
return errors.New("invalid runtime config")
}
return run(ctx, context.Args().First(),
context.String("bundle"),
context.String("console"),
context.String("console-socket"),
context.String("pid-file"),
context.Bool("detach"),
context.Bool("systemd-cgroup"),
runtimeConfig)
},
}
func run(ctx context.Context, containerID, bundle, console, consoleSocket, pidFile string, detach, systemdCgroup bool,
runtimeConfig oci.RuntimeConfig) error {
span, ctx := katautils.Trace(ctx, "run")
defer span.Finish()
consolePath, err := setupConsole(console, consoleSocket)
if err != nil {
return err
}
if err := create(ctx, containerID, bundle, consolePath, pidFile, detach, systemdCgroup, runtimeConfig); err != nil {
return err
}
sandbox, err := start(ctx, containerID)
if err != nil {
return err
}
if detach {
return nil
}
containers := sandbox.GetAllContainers()
if len(containers) == 0 {
return fmt.Errorf("There are no containers running in the sandbox: %s", sandbox.ID())
}
p, err := os.FindProcess(containers[0].GetPid())
if err != nil {
return err
}
ps, err := p.Wait()
if err != nil {
return fmt.Errorf("Process state %s: %s", ps.String(), err)
}
// delete container's resources
if err := delete(ctx, sandbox.ID(), true); err != nil {
return err
}
//runtime should forward container exit code to the system
return cli.NewExitError("", ps.Sys().(syscall.WaitStatus).ExitStatus())
}

View File

@ -1,670 +0,0 @@
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"flag"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"testing"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli"
ktu "github.com/kata-containers/kata-containers/src/runtime/pkg/katatestutils"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
vcAnnotations "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/compatoci"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/oci"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/vcmock"
)
func TestRunCliAction(t *testing.T) {
assert := assert.New(t)
flagSet := flag.NewFlagSet("flag", flag.ContinueOnError)
flagSet.Parse([]string{"runtime"})
// create a new fake context
ctx := createCLIContext(flagSet)
// get Action function
actionFunc, ok := runCLICommand.Action.(func(ctx *cli.Context) error)
assert.True(ok)
err := actionFunc(ctx)
assert.Error(err, "missing runtime configuration")
// temporal dir to place container files
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
// create a new runtime config
runtimeConfig, err := newTestRuntimeConfig(tmpdir, "/dev/ptmx", true)
assert.NoError(err)
ctx.App.Metadata = map[string]interface{}{
"runtimeConfig": runtimeConfig,
}
err = actionFunc(ctx)
assert.Error(err, "run without args")
}
func TestRunInvalidArgs(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
MockContainers: []*vcmock.Container{
{MockID: testContainerID},
},
}
// fake functions used to run containers
testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) {
return sandbox, nil
}
testingImpl.StartSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) {
return sandbox, nil
}
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
defer func() {
testingImpl.CreateSandboxFunc = nil
testingImpl.StartSandboxFunc = nil
}()
// temporal dir to place container files
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
// create a new bundle
bundlePath := filepath.Join(tmpdir, "bundle")
err = os.MkdirAll(bundlePath, testDirMode)
assert.NoError(err)
err = makeOCIBundle(bundlePath)
assert.NoError(err)
// pid file
pidFilePath := filepath.Join(tmpdir, "pid")
// console file
consolePath := "/dev/ptmx"
// inexistent path
inexistentPath := "/this/path/does/not/exist"
runtimeConfig, err := newTestRuntimeConfig(tmpdir, consolePath, true)
assert.NoError(err)
type testArgs struct {
containerID string
bundle string
console string
consoleSocket string
pidFile string
detach bool
systemdCgroup bool
runtimeConfig oci.RuntimeConfig
}
args := []testArgs{
{"", "", "", "", "", true, true, oci.RuntimeConfig{}},
{"", "", "", "", "", false, false, oci.RuntimeConfig{}},
{"", "", "", "", "", true, false, runtimeConfig},
{"", "", "", "", "", false, true, runtimeConfig},
{"", "", "", "", pidFilePath, false, false, runtimeConfig},
{"", "", "", "", inexistentPath, false, false, runtimeConfig},
{"", "", "", "", pidFilePath, false, true, runtimeConfig},
{"", "", "", inexistentPath, pidFilePath, false, true, runtimeConfig},
{"", "", inexistentPath, inexistentPath, pidFilePath, false, false, runtimeConfig},
{"", "", inexistentPath, "", pidFilePath, false, false, runtimeConfig},
{"", "", consolePath, "", pidFilePath, false, true, runtimeConfig},
{"", bundlePath, consolePath, "", pidFilePath, false, true, runtimeConfig},
{testContainerID, inexistentPath, consolePath, "", pidFilePath, false, true, oci.RuntimeConfig{}},
{testContainerID, inexistentPath, consolePath, "", inexistentPath, false, true, oci.RuntimeConfig{}},
{testContainerID, bundlePath, consolePath, "", pidFilePath, false, false, oci.RuntimeConfig{}},
{testContainerID, inexistentPath, consolePath, "", pidFilePath, false, false, runtimeConfig},
{testContainerID, inexistentPath, consolePath, "", inexistentPath, false, true, runtimeConfig},
{testContainerID, bundlePath, consolePath, "", pidFilePath, false, true, runtimeConfig},
}
for i, a := range args {
err := run(context.Background(), a.containerID, a.bundle, a.console, a.consoleSocket, a.pidFile, a.detach, a.systemdCgroup, a.runtimeConfig)
assert.Errorf(err, "test %d (%+v)", i, a)
}
}
type runContainerData struct {
pidFilePath string
consolePath string
bundlePath string
spec *specs.Spec
sandbox *vcmock.Sandbox
runtimeConfig oci.RuntimeConfig
process *os.Process
tmpDir string
}
func testRunContainerSetup(t *testing.T) runContainerData {
assert := assert.New(t)
// create a fake container workload
workload := []string{"/bin/sleep", "10"}
cmd := exec.Command(workload[0], workload[1:]...)
err := cmd.Start()
assert.NoError(err, "unable to start fake container workload %+v: %s", workload, err)
// temporal dir to place container files
// Note - it is returned to the caller, who does the defer remove to clean up.
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
// pid file
pidFilePath := filepath.Join(tmpdir, "pid")
// console file
consolePath := "/dev/ptmx"
// create a new bundle
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
// sandbox id and container id must be the same otherwise delete will not works
sandbox := &vcmock.Sandbox{
MockID: testContainerID,
}
sandbox.MockContainers = []*vcmock.Container{
{
MockID: testContainerID,
MockPid: cmd.Process.Pid,
MockSandbox: sandbox,
},
}
// create a new runtime config
runtimeConfig, err := newTestRuntimeConfig(tmpdir, consolePath, true)
assert.NoError(err)
ociSpec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
return runContainerData{
pidFilePath: pidFilePath,
consolePath: consolePath,
bundlePath: bundlePath,
sandbox: sandbox,
spec: &ociSpec,
runtimeConfig: runtimeConfig,
process: cmd.Process,
tmpDir: tmpdir,
}
}
func TestRunContainerSuccessful(t *testing.T) {
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(ktu.TestDisabledNeedRoot)
}
assert := assert.New(t)
d := testRunContainerSetup(t)
defer os.RemoveAll(d.tmpDir)
// this flags is used to detect if createSandboxFunc was called
flagCreate := false
// fake functions used to run containers
testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) {
flagCreate = true
return d.sandbox, nil
}
testingImpl.StartSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) {
return d.sandbox, nil
}
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
// return an empty list on create
if !flagCreate {
return vc.ContainerStatus{}, nil
}
// return a sandboxStatus with the container status
return vc.ContainerStatus{
ID: d.sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
},
Spec: d.spec,
}, nil
}
testingImpl.StartContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) {
// now we can kill the fake container workload
err := d.process.Kill()
assert.NoError(err)
return d.sandbox.MockContainers[0], nil
}
testingImpl.DeleteSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) {
return d.sandbox, nil
}
testingImpl.DeleteContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) {
return d.sandbox.MockContainers[0], nil
}
defer func() {
testingImpl.CreateSandboxFunc = nil
testingImpl.StartSandboxFunc = nil
testingImpl.StatusContainerFunc = nil
testingImpl.StartContainerFunc = nil
testingImpl.DeleteSandboxFunc = nil
testingImpl.DeleteContainerFunc = nil
}()
type errorTestArgs struct {
bundlePath string
// regex string for text of error message
errorRE string
// If true, expect a cli.ExitError, else expect any other type
// of error.
expectExitError bool
}
args := []errorTestArgs{
{"", "config.json: no such file or directory", false},
{d.bundlePath, "", true},
}
for i, a := range args {
err = run(context.Background(), d.sandbox.ID(), a.bundlePath, d.consolePath, "", d.pidFilePath, false, true, d.runtimeConfig)
assert.Errorf(err, "test args %d (%+v)", i, a)
if a.errorRE == "" {
assert.Empty(err.Error())
} else {
re := regexp.MustCompile(a.errorRE)
matches := re.FindAllStringSubmatch(err.Error(), -1)
assert.NotEmpty(matches)
}
e, ok := err.(*cli.ExitError)
if a.expectExitError {
// should return ExitError with the message and exit code
assert.Truef(ok, "test args %d (%+v): error should be a cli.ExitError: %s", i, a, err)
assert.NotZero(e.ExitCode())
} else {
assert.Falsef(ok, "test args %d (%+v): error should not be a cli.ExitError: %s", i, a, err)
}
}
}
func TestRunContainerDetachSuccessful(t *testing.T) {
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(ktu.TestDisabledNeedRoot)
}
assert := assert.New(t)
d := testRunContainerSetup(t)
defer os.RemoveAll(d.tmpDir)
// this flags is used to detect if createSandboxFunc was called
flagCreate := false
// fake functions used to run containers
testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) {
flagCreate = true
return d.sandbox, nil
}
testingImpl.StartSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) {
return d.sandbox, nil
}
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
// return an empty list on create
if !flagCreate {
return vc.ContainerStatus{}, nil
}
// return a sandboxStatus with the container status
return vc.ContainerStatus{
ID: d.sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
},
Spec: d.spec,
}, nil
}
testingImpl.StartContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) {
// now we can kill the fake container workload
err := d.process.Kill()
assert.NoError(err)
return d.sandbox.MockContainers[0], nil
}
testingImpl.DeleteSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) {
return d.sandbox, nil
}
testingImpl.DeleteContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) {
return d.sandbox.MockContainers[0], nil
}
defer func() {
testingImpl.CreateSandboxFunc = nil
testingImpl.StartSandboxFunc = nil
testingImpl.StatusContainerFunc = nil
testingImpl.StartContainerFunc = nil
testingImpl.DeleteSandboxFunc = nil
testingImpl.DeleteContainerFunc = nil
}()
err = run(context.Background(), d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, true, true, d.runtimeConfig)
// should not return ExitError
assert.NoError(err)
}
func TestRunContainerDeleteFail(t *testing.T) {
assert := assert.New(t)
d := testRunContainerSetup(t)
defer os.RemoveAll(d.tmpDir)
// this flags is used to detect if createSandboxFunc was called
flagCreate := false
// fake functions used to run containers
testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) {
flagCreate = true
return d.sandbox, nil
}
testingImpl.StartSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) {
return d.sandbox, nil
}
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
// return an empty list on create
if !flagCreate {
return vc.ContainerStatus{}, nil
}
// return a sandboxStatus with the container status
return vc.ContainerStatus{
ID: d.sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
},
Spec: d.spec,
}, nil
}
testingImpl.StartContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) {
// now we can kill the fake container workload
err := d.process.Kill()
assert.NoError(err)
return d.sandbox.MockContainers[0], nil
}
testingImpl.DeleteSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) {
// return an error to provoke a failure in delete
return nil, fmt.Errorf("DeleteSandboxFunc")
}
testingImpl.DeleteContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) {
// return an error to provoke a failure in delete
return d.sandbox.MockContainers[0], fmt.Errorf("DeleteContainerFunc")
}
defer func() {
testingImpl.CreateSandboxFunc = nil
testingImpl.StartSandboxFunc = nil
testingImpl.StatusContainerFunc = nil
testingImpl.StartContainerFunc = nil
testingImpl.DeleteSandboxFunc = nil
testingImpl.DeleteContainerFunc = nil
}()
err = run(context.Background(), d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, false, true, d.runtimeConfig)
// should not return ExitError
err, ok := err.(*cli.ExitError)
assert.False(ok, "error should not be a cli.ExitError: %s", err)
}
func TestRunContainerWaitFail(t *testing.T) {
assert := assert.New(t)
d := testRunContainerSetup(t)
defer os.RemoveAll(d.tmpDir)
// this flags is used to detect if createSandboxFunc was called
flagCreate := false
// fake functions used to run containers
testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) {
flagCreate = true
return d.sandbox, nil
}
testingImpl.StartSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) {
return d.sandbox, nil
}
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
// return an empty list on create
if !flagCreate {
return vc.ContainerStatus{}, nil
}
// return a sandboxStatus with the container status
return vc.ContainerStatus{
ID: d.sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
},
Spec: d.spec,
}, nil
}
testingImpl.StartContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) {
// now we can kill the fake container workload
err := d.process.Kill()
assert.NoError(err)
// change PID to provoke a failure in Wait
d.sandbox.MockContainers[0].MockPid = -1
return d.sandbox.MockContainers[0], nil
}
testingImpl.DeleteSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) {
// return an error to provoke a failure in delete
return nil, fmt.Errorf("DeleteSandboxFunc")
}
testingImpl.DeleteContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) {
// return an error to provoke a failure in delete
return d.sandbox.MockContainers[0], fmt.Errorf("DeleteContainerFunc")
}
defer func() {
testingImpl.CreateSandboxFunc = nil
testingImpl.StartSandboxFunc = nil
testingImpl.StatusContainerFunc = nil
testingImpl.StartContainerFunc = nil
testingImpl.DeleteSandboxFunc = nil
testingImpl.DeleteContainerFunc = nil
}()
err = run(context.Background(), d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, false, true, d.runtimeConfig)
// should not return ExitError
err, ok := err.(*cli.ExitError)
assert.False(ok, "error should not be a cli.ExitError: %s", err)
}
func TestRunContainerStartFail(t *testing.T) {
assert := assert.New(t)
d := testRunContainerSetup(t)
defer os.RemoveAll(d.tmpDir)
// now we can kill the fake container workload
err := d.process.Kill()
assert.NoError(err)
// this flags is used to detect if createSandboxFunc was called
flagCreate := false
// fake functions used to run containers
testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) {
flagCreate = true
return d.sandbox, nil
}
testingImpl.StartSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) {
// start fails
return nil, fmt.Errorf("StartSandbox")
}
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
// return an empty list on create
if !flagCreate {
return vc.ContainerStatus{}, nil
}
// return a sandboxStatus with the container status
return vc.ContainerStatus{
ID: d.sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
},
Spec: d.spec,
}, nil
}
defer func() {
testingImpl.CreateSandboxFunc = nil
testingImpl.StartSandboxFunc = nil
testingImpl.StatusContainerFunc = nil
}()
err = run(context.Background(), d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, false, true, d.runtimeConfig)
// should not return ExitError
err, ok := err.(*cli.ExitError)
assert.False(ok, "error should not be a cli.ExitError: %s", err)
}
func TestRunContainerStartFailExistingContainer(t *testing.T) {
assert := assert.New(t)
d := testRunContainerSetup(t)
defer os.RemoveAll(d.tmpDir)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
sandbox.MockContainers = []*vcmock.Container{
{
MockID: testContainerID,
MockSandbox: sandbox,
},
}
path, err := createTempContainerIDMapping(testContainerID, sandbox.ID())
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
// return the container status
return vc.ContainerStatus{
ID: testContainerID,
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
},
}, nil
}
testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) {
return sandbox, nil
}
testingImpl.StartSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) {
// force no containers
sandbox.MockContainers = nil
return sandbox, nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
testingImpl.CreateSandboxFunc = nil
testingImpl.StartSandboxFunc = nil
}()
err = run(context.Background(), d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, false, true, d.runtimeConfig)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}

View File

@ -1,114 +0,0 @@
// Copyright (c) 2014,2015,2016,2017 Docker, Inc.
// Copyright (c) 2018 Huawei Corporation.
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
"github.com/opencontainers/runc/libcontainer/specconv"
"github.com/urfave/cli"
)
var specCLICommand = cli.Command{
Name: "spec",
Usage: "create a new specification file",
ArgsUsage: "",
Description: `The spec command creates the new specification file named "` + specConfig + `" for
the bundle.
The spec generated is just a starter file. Editing of the spec is required to
achieve desired results. For example, the newly generated spec includes an args
parameter that is initially set to call the "sh" command when the container is
started. Calling "sh" may work for an ubuntu container or busybox, but will not
work for containers that do not include the "sh" program.
EXAMPLE:
To run docker's hello-world container one needs to set the args parameter
in the spec to call hello. This can be done using the sed command or a text
editor. The following commands create a bundle for hello-world, change the
default args parameter in the spec from "sh" to "/hello", then run the hello
command in a new hello-world container named container1:
mkdir hello
cd hello
docker pull hello-world
docker export $(docker create hello-world) > hello-world.tar
mkdir rootfs
tar -C rootfs -xf hello-world.tar
kata-runtime spec
sed -i 's;"sh";"/hello";' ` + specConfig + `
kata-runtime run container1
In the run command above, "container1" is the name for the instance of the
container that you are starting. The name you provide for the container instance
must be unique on your host.
An alternative for generating a customized spec config is to use "oci-runtime-tool", the
sub-command "oci-runtime-tool generate" has lots of options that can be used to do any
customizations as you want, see runtime-tools (https://github.com/opencontainers/runtime-tools)
to get more information.
When starting a container through kata-runtime, kata-runtime needs root privilege. If not
already running as root, you can use sudo to give kata-runtime root privilege. For
example: "sudo kata-runtime start container1" will give kata-runtime root privilege to start the
container on your host.
Alternatively, you can start a rootless container, which has the ability to run
without root privileges. For this to work, the specification file needs to be
adjusted accordingly. You can pass the parameter --rootless to this command to
generate a proper rootless spec file.`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "bundle, b",
Value: "",
Usage: "path to the root of the bundle directory",
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
span, _ := katautils.Trace(ctx, "spec")
defer span.Finish()
spec := specconv.Example()
checkNoFile := func(name string) error {
_, err := os.Stat(name)
if err == nil {
return fmt.Errorf("File %s exists. Remove it first", name)
}
if !os.IsNotExist(err) {
return err
}
return nil
}
bundle := context.String("bundle")
if bundle != "" {
if err := os.Chdir(bundle); err != nil {
return err
}
}
if err := checkNoFile(specConfig); err != nil {
return err
}
data, err := json.MarshalIndent(spec, "", "\t")
if err != nil {
return err
}
if err := ioutil.WriteFile(specConfig, data, 0640); err != nil {
return err
}
return nil
},
}

View File

@ -1,35 +0,0 @@
// Copyright (c) 2018 Huawei Corporation.
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"flag"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli"
)
func TestSpecCliAction(t *testing.T) {
assert := assert.New(t)
actionFunc, ok := specCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
flagSet := flag.NewFlagSet("flag", flag.ContinueOnError)
ctx := createCLIContext(flagSet)
defer os.Remove(specConfig)
err := actionFunc(ctx)
assert.NoError(err)
pattern := "gid=5"
patternRootless := "uidMappings"
err = grep(pattern, specConfig)
assert.NoError(err)
err = grep(patternRootless, specConfig)
assert.Error(err)
}

View File

@ -1,113 +0,0 @@
// Copyright (c) 2014,2015,2016 Docker, Inc.
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"fmt"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
vcAnnot "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/oci"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var startCLICommand = cli.Command{
Name: "start",
Usage: "executes the user defined process in a created container",
ArgsUsage: `<container-id> [container-id...]
<container-id> is your name for the instance of the container that you
are starting. The name you provide for the container instance must be
unique on your host.`,
Description: `The start command executes the user defined process in a created container .`,
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
args := context.Args()
if !args.Present() {
return fmt.Errorf("Missing container ID, should at least provide one")
}
for _, cID := range []string(args) {
if _, err := start(ctx, cID); err != nil {
return err
}
}
return nil
},
}
func start(ctx context.Context, containerID string) (vc.VCSandbox, error) {
span, _ := katautils.Trace(ctx, "start")
defer span.Finish()
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(ctx, kataLog)
span.SetTag("container", containerID)
// Checks the MUST and MUST NOT from OCI runtime specification
status, sandboxID, err := getExistingContainerInfo(ctx, containerID)
if err != nil {
return nil, err
}
containerID = status.ID
kataLog = kataLog.WithFields(logrus.Fields{
"container": containerID,
"sandbox": sandboxID,
})
setExternalLoggers(ctx, kataLog)
span.SetTag("container", containerID)
span.SetTag("sandbox", sandboxID)
containerType, err := oci.GetContainerType(status.Annotations)
if err != nil {
return nil, err
}
ociSpec, err := oci.GetOCIConfig(status)
if err != nil {
return nil, err
}
var sandbox vc.VCSandbox
if containerType.IsSandbox() {
s, err := vci.StartSandbox(ctx, sandboxID)
if err != nil {
return nil, err
}
sandbox = s
} else {
c, err := vci.StartContainer(ctx, sandboxID, containerID)
if err != nil {
return nil, err
}
sandbox = c.Sandbox()
}
// Run post-start OCI hooks.
err = katautils.EnterNetNS(sandbox.GetNetNs(), func() error {
return katautils.PostStartHooks(ctx, ociSpec, sandboxID, status.Annotations[vcAnnot.BundlePathKey])
})
if err != nil {
return nil, err
}
return sandbox, nil
}

View File

@ -1,243 +0,0 @@
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"flag"
"io/ioutil"
"os"
"testing"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
vcAnnotations "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/vcmock"
)
func TestStartInvalidArgs(t *testing.T) {
assert := assert.New(t)
// Missing container id
_, err := start(context.Background(), "")
assert.Error(err)
assert.False(vcmock.IsMockError(err))
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
// Mock StatusContainer error
_, err = start(context.Background(), testContainerID)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
path, err = ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
katautils.SetCtrsMapTreePath(path)
// Container missing in container mapping
_, err = start(context.Background(), testContainerID)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
func TestStartSandbox(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID())
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
},
Spec: &specs.Spec{},
}, nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
_, err = start(context.Background(), sandbox.ID())
assert.Error(err)
assert.True(vcmock.IsMockError(err))
testingImpl.StartSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) {
return sandbox, nil
}
defer func() {
testingImpl.StartSandboxFunc = nil
}()
_, err = start(context.Background(), sandbox.ID())
assert.Nil(err)
}
func TestStartMissingAnnotation(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID())
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: sandbox.ID(),
Annotations: map[string]string{},
}, nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
_, err = start(context.Background(), sandbox.ID())
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
func TestStartContainerSucessFailure(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
sandbox.MockContainers = []*vcmock.Container{
{
MockID: testContainerID,
MockSandbox: sandbox,
},
}
path, err := createTempContainerIDMapping(testContainerID, sandbox.ID())
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: testContainerID,
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
},
Spec: &specs.Spec{},
}, nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
_, err = start(context.Background(), testContainerID)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
testingImpl.StartContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) {
return sandbox.MockContainers[0], nil
}
defer func() {
testingImpl.StartContainerFunc = nil
}()
_, err = start(context.Background(), testContainerID)
assert.Nil(err)
}
func TestStartCLIFunction(t *testing.T) {
assert := assert.New(t)
flagSet := &flag.FlagSet{}
ctx := createCLIContext(flagSet)
fn, ok := startCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
// no container id in the Metadata
err := fn(ctx)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
path, err := createTempContainerIDMapping("xyz", "xyz")
assert.NoError(err)
defer os.RemoveAll(path)
flagSet = flag.NewFlagSet("container-id", flag.ContinueOnError)
flagSet.Parse([]string{"xyz"})
ctx = createCLIContext(flagSet)
err = fn(ctx)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
}
func TestStartCLIFunctionSuccess(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
}
sandbox.MockContainers = []*vcmock.Container{
{
MockID: testContainerID,
MockSandbox: sandbox,
},
}
path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: testContainerID,
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
},
Spec: &specs.Spec{},
}, nil
}
testingImpl.StartContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) {
return sandbox.MockContainers[0], nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
testingImpl.StartContainerFunc = nil
}()
fn, ok := startCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
flagSet := flag.NewFlagSet("test", 0)
flagSet.Parse([]string{testContainerID})
ctx := createCLIContext(flagSet)
assert.NotNil(ctx)
err = fn(ctx)
assert.NoError(err)
}

View File

@ -1,70 +0,0 @@
// Copyright (c) 2014,2015,2016 Docker, Inc.
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"encoding/json"
"fmt"
"os"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/oci"
"github.com/urfave/cli"
)
var stateCLICommand = cli.Command{
Name: "state",
Usage: "output the state of a container",
ArgsUsage: `<container-id>
<container-id> is your name for the instance of the container`,
Description: `The state command outputs current state information for the
instance of a container.`,
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
args := context.Args()
if len(args) != 1 {
return fmt.Errorf("Expecting only one container ID, got %d: %v", len(args), []string(args))
}
return state(ctx, args.First())
},
}
func state(ctx context.Context, containerID string) error {
span, _ := katautils.Trace(ctx, "state")
defer span.Finish()
kataLog = kataLog.WithField("container", containerID)
span.SetTag("container", containerID)
setExternalLoggers(ctx, kataLog)
// Checks the MUST and MUST NOT from OCI runtime specification
status, _, err := getExistingContainerInfo(ctx, containerID)
if err != nil {
return err
}
// Convert the status to the expected State structure
state := oci.StatusToOCIState(status)
stateJSON, err := json.MarshalIndent(state, "", " ")
if err != nil {
return err
}
// Print stateJSON to stdout
fmt.Fprintf(os.Stdout, "%s", stateJSON)
return nil
}

View File

@ -1,85 +0,0 @@
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"flag"
"io/ioutil"
"os"
"testing"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
vcAnnotations "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/vcmock"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli"
)
func TestStateCliAction(t *testing.T) {
assert := assert.New(t)
actionFunc, ok := stateCLICommand.Action.(func(ctx *cli.Context) error)
assert.True(ok)
flagSet := flag.NewFlagSet("flag", flag.ContinueOnError)
// without container id
flagSet.Parse([]string{"runtime"})
ctx := createCLIContext(flagSet)
err := actionFunc(ctx)
assert.Error(err)
// with container id
flagSet.Parse([]string{"runtime", testContainerID})
ctx = createCLIContext(flagSet)
err = actionFunc(ctx)
assert.Error(err)
}
func TestStateSuccessful(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testContainerID,
}
sandbox.MockContainers = []*vcmock.Container{
{
MockID: sandbox.ID(),
MockSandbox: sandbox,
},
}
path, err := ioutil.TempDir("", "containers-mapping")
assert.NoError(err)
defer os.RemoveAll(path)
ctrsMapTreePath = path
// trying with an inexistent id
err = state(context.Background(), "123456789")
assert.Error(err)
path, err = createTempContainerIDMapping(testContainerID, sandbox.ID())
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: testContainerID,
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
},
}, nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
err = state(context.Background(), testContainerID)
assert.NoError(err)
}

View File

@ -1,288 +0,0 @@
// Copyright (c) 2016,2017 Docker, Inc.
// Copyright (c) 2018 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"encoding/json"
"fmt"
"os"
"strconv"
"github.com/docker/go-units"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
func i64Ptr(i int64) *int64 { return &i }
func u64Ptr(i uint64) *uint64 { return &i }
func u16Ptr(i uint16) *uint16 { return &i }
var updateCLICommand = cli.Command{
Name: "update",
Usage: "update container resource constraints",
ArgsUsage: `<container-id>`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "resources, r",
Value: "",
Usage: `path to the file containing the resources to update or '-' to read from the standard input
The accepted format is as follow (unchanged values can be omitted):
{
"memory": {
"limit": 0,
"reservation": 0,
"swap": 0,
"kernel": 0,
"kernelTCP": 0
},
"cpu": {
"shares": 0,
"quota": 0,
"period": 0,
"realtimeRuntime": 0,
"realtimePeriod": 0,
"cpus": "",
"mems": ""
},
"blockIO": {
"weight": 0
},
"pids": {
"limit": 0
}
}
Note: if data is to be read from a file or the standard input, all
other options are ignored.
`,
},
cli.IntFlag{
Name: "blkio-weight",
Usage: "Specifies per cgroup weight, range is from 10 to 1000",
},
cli.StringFlag{
Name: "cpu-period",
Usage: "CPU CFS period to be used for hardcapping (in usecs). 0 to use system default",
},
cli.StringFlag{
Name: "cpu-quota",
Usage: "CPU CFS hardcap limit (in usecs). Allowed cpu time in a given period",
},
cli.StringFlag{
Name: "cpu-share",
Usage: "CPU shares (relative weight vs. other containers)",
},
cli.StringFlag{
Name: "cpu-rt-period",
Usage: "CPU realtime period to be used for hardcapping (in usecs). 0 to use system default",
},
cli.StringFlag{
Name: "cpu-rt-runtime",
Usage: "CPU realtime hardcap limit (in usecs). Allowed cpu time in a given period",
},
cli.StringFlag{
Name: "cpuset-cpus",
Usage: "CPU(s) to use",
},
cli.StringFlag{
Name: "cpuset-mems",
Usage: "Memory node(s) to use",
},
cli.StringFlag{
Name: "kernel-memory",
Usage: "Kernel memory limit (in bytes)",
},
cli.StringFlag{
Name: "kernel-memory-tcp",
Usage: "Kernel memory limit (in bytes) for tcp buffer",
},
cli.StringFlag{
Name: "memory",
Usage: "Memory limit (in bytes)",
},
cli.StringFlag{
Name: "memory-reservation",
Usage: "Memory reservation or soft_limit (in bytes)",
},
cli.StringFlag{
Name: "memory-swap",
Usage: "Total memory usage (memory + swap); set '-1' to enable unlimited swap",
},
cli.IntFlag{
Name: "pids-limit",
Usage: "Maximum number of pids allowed in the container",
},
cli.StringFlag{
Name: "l3-cache-schema",
Usage: "The string of Intel RDT/CAT L3 cache schema",
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
span, _ := katautils.Trace(ctx, "update")
defer span.Finish()
if !context.Args().Present() {
return fmt.Errorf("Missing container ID, should at least provide one")
}
containerID := context.Args().First()
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(ctx, kataLog)
span.SetTag("container", containerID)
status, sandboxID, err := getExistingContainerInfo(ctx, containerID)
if err != nil {
return err
}
containerID = status.ID
kataLog = kataLog.WithFields(logrus.Fields{
"container": containerID,
"sandbox": sandboxID,
})
setExternalLoggers(ctx, kataLog)
span.SetTag("container", containerID)
span.SetTag("sandbox", sandboxID)
// container MUST be running
if state := status.State.State; !(state == types.StateRunning || state == types.StateReady) {
return fmt.Errorf("Container %s is not running or Ready, the state is %s", containerID, state)
}
r := specs.LinuxResources{
Memory: &specs.LinuxMemory{
Limit: i64Ptr(0),
Reservation: i64Ptr(0),
Swap: i64Ptr(0),
Kernel: i64Ptr(0),
KernelTCP: i64Ptr(0),
},
CPU: &specs.LinuxCPU{
Shares: u64Ptr(0),
Quota: i64Ptr(0),
Period: u64Ptr(0),
RealtimeRuntime: i64Ptr(0),
RealtimePeriod: u64Ptr(0),
Cpus: "",
Mems: "",
},
BlockIO: &specs.LinuxBlockIO{
Weight: u16Ptr(0),
},
Pids: &specs.LinuxPids{
Limit: 0,
},
}
if in := context.String("resources"); in != "" {
var (
f *os.File
err error
)
switch in {
case "-":
f = os.Stdin
default:
f, err = os.Open(in)
if err != nil {
return err
}
}
err = json.NewDecoder(f).Decode(&r)
if err != nil {
return err
}
} else {
if val := context.Int("blkio-weight"); val != 0 {
r.BlockIO.Weight = u16Ptr(uint16(val))
}
if val := context.String("cpuset-cpus"); val != "" {
r.CPU.Cpus = val
}
if val := context.String("cpuset-mems"); val != "" {
r.CPU.Mems = val
}
for _, pair := range []struct {
opt string
dest *uint64
}{
{"cpu-period", r.CPU.Period},
{"cpu-rt-period", r.CPU.RealtimePeriod},
{"cpu-share", r.CPU.Shares},
} {
if val := context.String(pair.opt); val != "" {
var err error
*pair.dest, err = strconv.ParseUint(val, 10, 64)
if err != nil {
return fmt.Errorf("invalid value for %s: %s", pair.opt, err)
}
}
}
for _, pair := range []struct {
opt string
dest *int64
}{
{"cpu-quota", r.CPU.Quota},
{"cpu-rt-runtime", r.CPU.RealtimeRuntime},
} {
if val := context.String(pair.opt); val != "" {
var err error
*pair.dest, err = strconv.ParseInt(val, 10, 64)
if err != nil {
return fmt.Errorf("invalid value for %s: %s", pair.opt, err)
}
}
}
for _, pair := range []struct {
opt string
dest *int64
}{
{"memory", r.Memory.Limit},
{"memory-swap", r.Memory.Swap},
{"kernel-memory", r.Memory.Kernel},
{"kernel-memory-tcp", r.Memory.KernelTCP},
{"memory-reservation", r.Memory.Reservation},
} {
if val := context.String(pair.opt); val != "" {
var v int64
if val != "-1" {
v, err = units.RAMInBytes(val)
if err != nil {
return fmt.Errorf("invalid value for %s: %s", pair.opt, err)
}
} else {
v = -1
}
*pair.dest = v
}
}
r.Pids.Limit = int64(context.Int("pids-limit"))
}
return vci.UpdateContainer(ctx, sandboxID, containerID, r)
},
}

View File

@ -1,208 +0,0 @@
// Copyright (c) 2018 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"flag"
"io/ioutil"
"os"
"testing"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
vcAnnotations "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/vcmock"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli"
)
func TestUpdateCLIAction(t *testing.T) {
assert := assert.New(t)
flagSet := flag.NewFlagSet("update", flag.ContinueOnError)
flagSet.Parse([]string{"resources"})
// create a new fake context
ctx := createCLIContext(flagSet)
// get Action function
actionFunc, ok := updateCLICommand.Action.(func(ctx *cli.Context) error)
assert.True(ok)
err := actionFunc(ctx)
assert.Error(err, "Missing container ID")
}
func TestUpdateCLIFailure(t *testing.T) {
assert := assert.New(t)
flagSet := flag.NewFlagSet("update", flag.ContinueOnError)
ctx := createCLIContext(flagSet)
actionFunc, ok := updateCLICommand.Action.(func(ctx *cli.Context) error)
assert.True(ok)
// missing container ID
err := actionFunc(ctx)
assert.Error(err)
// container info
flagSet.Parse([]string{testContainerID})
ctx = createCLIContext(flagSet)
err = actionFunc(ctx)
assert.Error(err)
// not running
sandbox := &vcmock.Sandbox{
MockID: testContainerID,
}
sandbox.MockContainers = []*vcmock.Container{
{
MockID: sandbox.ID(),
MockSandbox: sandbox,
},
}
path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID())
assert.NoError(err)
defer os.RemoveAll(path)
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
},
}, nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
}()
err = actionFunc(ctx)
assert.Error(err)
// resources file does not exist
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
},
State: types.ContainerState{
State: types.StateRunning,
},
}, nil
}
testingImpl.UpdateContainerFunc = func(ctx context.Context, sandboxID, containerID string, resources specs.LinuxResources) error {
return nil
}
defer func() {
testingImpl.UpdateContainerFunc = nil
}()
flagSet.String("resources", "/abc/123/xyz/rgb", "")
ctx = createCLIContext(flagSet)
err = actionFunc(ctx)
assert.Error(err)
// json decode error
f, err := ioutil.TempFile("", "resources")
defer os.Remove(f.Name())
assert.NoError(err)
assert.NotNil(f)
f.WriteString("no json")
f.Close()
flagSet.Set("resources", f.Name())
ctx = createCLIContext(flagSet)
err = actionFunc(ctx)
assert.Error(err)
// ParseUint Error
flagSet = flag.NewFlagSet("update", flag.ContinueOnError)
flagSet.Parse([]string{testContainerID})
flagSet.String("cpu-period", "abcxyz", "")
ctx = createCLIContext(flagSet)
err = actionFunc(ctx)
assert.Error(err)
// ParseInt Error
flagSet = flag.NewFlagSet("update", flag.ContinueOnError)
flagSet.Parse([]string{testContainerID})
flagSet.String("cpu-quota", "abcxyz", "")
ctx = createCLIContext(flagSet)
err = actionFunc(ctx)
assert.Error(err)
// RAMInBytes Error
flagSet = flag.NewFlagSet("update", flag.ContinueOnError)
flagSet.Parse([]string{testContainerID})
flagSet.String("memory", "abcxyz", "")
ctx = createCLIContext(flagSet)
err = actionFunc(ctx)
assert.Error(err)
}
func TestUpdateCLISuccessful(t *testing.T) {
assert := assert.New(t)
sandbox := &vcmock.Sandbox{
MockID: testContainerID,
}
sandbox.MockContainers = []*vcmock.Container{
{
MockID: sandbox.ID(),
MockSandbox: sandbox,
},
}
testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) {
return vc.ContainerStatus{
ID: sandbox.ID(),
Annotations: map[string]string{
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
},
State: types.ContainerState{
State: types.StateRunning,
},
}, nil
}
testingImpl.UpdateContainerFunc = func(ctx context.Context, sandboxID, containerID string, resources specs.LinuxResources) error {
return nil
}
defer func() {
testingImpl.StatusContainerFunc = nil
testingImpl.UpdateContainerFunc = nil
}()
path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID())
assert.NoError(err)
defer os.RemoveAll(path)
actionFunc, ok := updateCLICommand.Action.(func(ctx *cli.Context) error)
assert.True(ok)
flagSet := flag.NewFlagSet("update", flag.ContinueOnError)
flagSet.Parse([]string{testContainerID})
flagSet.Int("blkio-weight", 20, "")
flagSet.String("cpuset-cpus", "0-5", "")
flagSet.String("cpuset-mems", "0-5", "")
flagSet.String("cpu-period", "1000", "")
flagSet.String("cpu-rt-period", "1000", "")
flagSet.String("cpu-share", "1000", "")
flagSet.String("cpu-quota", "1000", "")
flagSet.String("cpu-rt-runtime", "1000", "")
flagSet.String("memory", "100M", "")
flagSet.String("memory-swap", "100M", "")
flagSet.String("kernel-memory", "100M", "")
flagSet.String("kernel-memory-tcp", "100M", "")
flagSet.String("memory-reservation", "100M", "")
ctx := createCLIContext(flagSet)
err = actionFunc(ctx)
assert.NoError(err)
}

View File

@ -83,7 +83,7 @@ func create(ctx context.Context, s *service, r *taskAPI.CreateTaskRequest) (*con
// ctx will be canceled after this rpc service call, but the sandbox will live
// across multiple rpc service calls.
//
sandbox, _, err := katautils.CreateSandbox(s.ctx, vci, *ociSpec, *s.config, rootFs, r.ID, bundlePath, "", disableOutput, false, true)
sandbox, _, err := katautils.CreateSandbox(s.ctx, vci, *ociSpec, *s.config, rootFs, r.ID, bundlePath, "", disableOutput, false)
if err != nil {
return nil, err
}
@ -107,7 +107,7 @@ func create(ctx context.Context, s *service, r *taskAPI.CreateTaskRequest) (*con
}
}()
_, err = katautils.CreateContainer(ctx, vci, s.sandbox, *ociSpec, rootFs, r.ID, bundlePath, "", disableOutput, true)
_, err = katautils.CreateContainer(ctx, s.sandbox, *ociSpec, rootFs, r.ID, bundlePath, "", disableOutput)
if err != nil {
return nil, err
}

View File

@ -211,16 +211,11 @@ func TestCreateContainerSuccess(t *testing.T) {
sandbox := &vcmock.Sandbox{
MockID: testSandboxID,
CreateContainerFunc: func(containerConfig vc.ContainerConfig) (vc.VCContainer, error) {
return &vcmock.Container{}, nil
},
}
testingImpl.CreateContainerFunc = func(ctx context.Context, sandboxID string, containerConfig vc.ContainerConfig) (vc.VCSandbox, vc.VCContainer, error) {
return sandbox, &vcmock.Container{}, nil
}
defer func() {
testingImpl.CreateContainerFunc = nil
}()
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)

View File

@ -104,7 +104,7 @@ func SetEphemeralStorageType(ociSpec specs.Spec) specs.Spec {
// CreateSandbox create a sandbox container
func CreateSandbox(ctx context.Context, vci vc.VC, ociSpec specs.Spec, runtimeConfig oci.RuntimeConfig, rootFs vc.RootFs,
containerID, bundlePath, console string, disableOutput, systemdCgroup, builtIn bool) (_ vc.VCSandbox, _ vc.Process, err error) {
containerID, bundlePath, console string, disableOutput, systemdCgroup bool) (_ vc.VCSandbox, _ vc.Process, err error) {
span, ctx := Trace(ctx, "createSandbox")
defer span.Finish()
@ -113,9 +113,7 @@ func CreateSandbox(ctx context.Context, vci vc.VC, ociSpec specs.Spec, runtimeCo
return nil, vc.Process{}, err
}
if builtIn {
sandboxConfig.Stateful = true
}
sandboxConfig.Stateful = true
if err := checkForFIPS(&sandboxConfig); err != nil {
return nil, vc.Process{}, err
@ -171,13 +169,6 @@ func CreateSandbox(ctx context.Context, vci vc.VC, ociSpec specs.Spec, runtimeCo
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
}
@ -211,7 +202,7 @@ func checkForFIPS(sandboxConfig *vc.SandboxConfig) error {
}
// CreateContainer create a container
func CreateContainer(ctx context.Context, vci vc.VC, sandbox vc.VCSandbox, ociSpec specs.Spec, rootFs vc.RootFs, containerID, bundlePath, console string, disableOutput, builtIn bool) (vc.Process, error) {
func CreateContainer(ctx context.Context, sandbox vc.VCSandbox, ociSpec specs.Spec, rootFs vc.RootFs, containerID, bundlePath, console string, disableOutput bool) (vc.Process, error) {
var c vc.VCContainer
span, ctx := Trace(ctx, "createContainer")
@ -242,22 +233,9 @@ func CreateContainer(ctx context.Context, vci vc.VC, sandbox vc.VCSandbox, ociSp
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
}
c, err = sandbox.CreateContainer(contConfig)
if err != nil {
return vc.Process{}, err
}
// Run pre-start OCI hooks.

View File

@ -40,6 +40,10 @@ var (
// testingImpl is a concrete mock RVC implementation used for testing
testingImpl = &vcmock.VCMock{}
// mock sandbox
mockSandbox = &vcmock.Sandbox{
MockID: testSandboxID,
}
tc ktu.TestConstraint
)
@ -258,11 +262,6 @@ func TestSetKernelParamsUserOptionTakesPriority(t *testing.T) {
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)
@ -295,7 +294,7 @@ func TestCreateSandboxConfigFail(t *testing.T) {
rootFs := vc.RootFs{Mounted: true}
_, _, err = CreateSandbox(context.Background(), testingImpl, spec, runtimeConfig, rootFs, testContainerID, bundlePath, testConsole, true, true, false)
_, _, err = CreateSandbox(context.Background(), testingImpl, spec, runtimeConfig, rootFs, testContainerID, bundlePath, testConsole, true, true)
assert.Error(err)
}
@ -306,11 +305,6 @@ func TestCreateSandboxFail(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)
@ -331,7 +325,7 @@ func TestCreateSandboxFail(t *testing.T) {
rootFs := vc.RootFs{Mounted: true}
_, _, err = CreateSandbox(context.Background(), testingImpl, spec, runtimeConfig, rootFs, testContainerID, bundlePath, testConsole, true, true, false)
_, _, err = CreateSandbox(context.Background(), testingImpl, spec, runtimeConfig, rootFs, testContainerID, bundlePath, testConsole, true, true)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
}
@ -381,11 +375,6 @@ func TestCheckForFips(t *testing.T) {
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)
@ -413,22 +402,16 @@ func TestCreateContainerContainerConfigFail(t *testing.T) {
rootFs := vc.RootFs{Mounted: true}
for _, disableOutput := range []bool{true, false} {
_, err = CreateContainer(context.Background(), testingImpl, nil, spec, rootFs, testContainerID, bundlePath, testConsole, disableOutput, false)
_, err = CreateContainer(context.Background(), mockSandbox, spec, rootFs, testContainerID, bundlePath, testConsole, disableOutput)
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)
@ -456,27 +439,21 @@ func TestCreateContainerFail(t *testing.T) {
rootFs := vc.RootFs{Mounted: true}
for _, disableOutput := range []bool{true, false} {
_, err = CreateContainer(context.Background(), testingImpl, nil, spec, rootFs, testContainerID, bundlePath, testConsole, disableOutput, false)
_, err = CreateContainer(context.Background(), mockSandbox, spec, rootFs, testContainerID, bundlePath, testConsole, disableOutput)
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
mockSandbox.CreateContainerFunc = func(containerConfig vc.ContainerConfig) (vc.VCContainer, error) {
return &vcmock.Container{}, nil
}
defer func() {
testingImpl.CreateContainerFunc = nil
mockSandbox.CreateContainerFunc = nil
}()
tmpdir, err := ioutil.TempDir("", "")
@ -506,8 +483,7 @@ func TestCreateContainer(t *testing.T) {
rootFs := vc.RootFs{Mounted: true}
for _, disableOutput := range []bool{true, false} {
_, err = CreateContainer(context.Background(), testingImpl, nil, spec, rootFs, testContainerID, bundlePath, testConsole, disableOutput, false)
_, err = CreateContainer(context.Background(), mockSandbox, spec, rootFs, testContainerID, bundlePath, testConsole, disableOutput)
assert.NoError(err)
os.RemoveAll(path)
}
}

View File

@ -1,110 +0,0 @@
// Copyright (c) 2018 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package katautils
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/rootless"
)
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
}
// doUpdatePath returns whether a ctrsMapTreePath needs to be updated with a rootless prefix
func doUpdatePath() bool {
return rootless.IsRootless() && !strings.HasPrefix(ctrsMapTreePath, rootless.GetRootlessDir())
}
// 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")
}
if doUpdatePath() {
SetCtrsMapTreePath(filepath.Join(rootless.GetRootlessDir(), ctrsMapTreePath))
}
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")
}
if doUpdatePath() {
SetCtrsMapTreePath(filepath.Join(rootless.GetRootlessDir(), ctrsMapTreePath))
}
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")
}
if doUpdatePath() {
SetCtrsMapTreePath(filepath.Join(rootless.GetRootlessDir(), ctrsMapTreePath))
}
path := filepath.Join(ctrsMapTreePath, containerID)
return os.RemoveAll(path)
}

View File

@ -1,140 +0,0 @@
// 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/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/rootless"
"github.com/stretchr/testify/assert"
)
func init() {
rootless.IsRootless = func() bool { return false }
}
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))
}

File diff suppressed because it is too large Load Diff

View File

@ -873,11 +873,17 @@ func Example_createAndStartSandbox() {
Containers: []vc.ContainerConfig{container},
}
_, err := vc.RunSandbox(sandboxConfig)
// Create the sandbox
s, err := vc.CreateSandbox(context.Background(), sandboxConfig, nil)
if err != nil {
fmt.Printf("Could not run sandbox: %s", err)
fmt.Printf("Could not create sandbox: %s", err)
return
}
return
// Start the sandbox
err = s.Start()
if err != nil {
fmt.Printf("Could not start sandbox: %s", err)
}
}
```

View File

@ -63,8 +63,16 @@ func Example_createAndStartSandbox() {
Containers: []vc.ContainerConfig{container},
}
_, err := vc.RunSandbox(context.Background(), sandboxConfig, nil)
// Create the sandbox
s, err := vc.CreateSandbox(context.Background(), sandboxConfig, nil)
if err != nil {
fmt.Printf("Could not run sandbox: %s", err)
fmt.Printf("Could not create sandbox: %s", err)
return
}
// Start the sandbox
err = s.Start()
if err != nil {
fmt.Printf("Could not start sandbox: %s", err)
}
}

View File

@ -11,13 +11,7 @@ package virtcontainers
import (
"context"
"syscall"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/api"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/config"
vcTypes "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/types"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
)
@ -41,137 +35,6 @@ func (impl *VCImpl) CreateSandbox(ctx context.Context, sandboxConfig SandboxConf
return CreateSandbox(ctx, sandboxConfig, impl.factory)
}
// DeleteSandbox implements the VC function of the same name.
func (impl *VCImpl) DeleteSandbox(ctx context.Context, sandboxID string) (VCSandbox, error) {
return DeleteSandbox(ctx, sandboxID)
}
// StartSandbox implements the VC function of the same name.
func (impl *VCImpl) StartSandbox(ctx context.Context, sandboxID string) (VCSandbox, error) {
return StartSandbox(ctx, sandboxID)
}
// StopSandbox implements the VC function of the same name.
func (impl *VCImpl) StopSandbox(ctx context.Context, sandboxID string, force bool) (VCSandbox, error) {
return StopSandbox(ctx, sandboxID, force)
}
// RunSandbox implements the VC function of the same name.
func (impl *VCImpl) RunSandbox(ctx context.Context, sandboxConfig SandboxConfig) (VCSandbox, error) {
return RunSandbox(ctx, sandboxConfig, impl.factory)
}
// ListSandbox implements the VC function of the same name.
func (impl *VCImpl) ListSandbox(ctx context.Context) ([]SandboxStatus, error) {
return ListSandbox(ctx)
}
// FetchSandbox will find out and connect to an existing sandbox and
// return the sandbox structure.
func (impl *VCImpl) FetchSandbox(ctx context.Context, sandboxID string) (VCSandbox, error) {
return FetchSandbox(ctx, sandboxID)
}
// StatusSandbox implements the VC function of the same name.
func (impl *VCImpl) StatusSandbox(ctx context.Context, sandboxID string) (SandboxStatus, error) {
return StatusSandbox(ctx, sandboxID)
}
// CreateContainer implements the VC function of the same name.
func (impl *VCImpl) CreateContainer(ctx context.Context, sandboxID string, containerConfig ContainerConfig) (VCSandbox, VCContainer, error) {
return CreateContainer(ctx, sandboxID, containerConfig)
}
// DeleteContainer implements the VC function of the same name.
func (impl *VCImpl) DeleteContainer(ctx context.Context, sandboxID, containerID string) (VCContainer, error) {
return DeleteContainer(ctx, sandboxID, containerID)
}
// StartContainer implements the VC function of the same name.
func (impl *VCImpl) StartContainer(ctx context.Context, sandboxID, containerID string) (VCContainer, error) {
return StartContainer(ctx, sandboxID, containerID)
}
// StopContainer implements the VC function of the same name.
func (impl *VCImpl) StopContainer(ctx context.Context, sandboxID, containerID string) (VCContainer, error) {
return StopContainer(ctx, sandboxID, containerID)
}
// EnterContainer implements the VC function of the same name.
func (impl *VCImpl) EnterContainer(ctx context.Context, sandboxID, containerID string, cmd types.Cmd) (VCSandbox, VCContainer, *Process, error) {
return EnterContainer(ctx, sandboxID, containerID, cmd)
}
// StatusContainer implements the VC function of the same name.
func (impl *VCImpl) StatusContainer(ctx context.Context, sandboxID, containerID string) (ContainerStatus, error) {
return StatusContainer(ctx, sandboxID, containerID)
}
// StatsContainer implements the VC function of the same name.
func (impl *VCImpl) StatsContainer(ctx context.Context, sandboxID, containerID string) (ContainerStats, error) {
return StatsContainer(ctx, sandboxID, containerID)
}
// StatsSandbox implements the VC function of the same name.
func (impl *VCImpl) StatsSandbox(ctx context.Context, sandboxID string) (SandboxStats, []ContainerStats, error) {
return StatsSandbox(ctx, sandboxID)
}
// KillContainer implements the VC function of the same name.
func (impl *VCImpl) KillContainer(ctx context.Context, sandboxID, containerID string, signal syscall.Signal, all bool) error {
return KillContainer(ctx, sandboxID, containerID, signal, all)
}
// ProcessListContainer implements the VC function of the same name.
func (impl *VCImpl) ProcessListContainer(ctx context.Context, sandboxID, containerID string, options ProcessListOptions) (ProcessList, error) {
return ProcessListContainer(ctx, sandboxID, containerID, options)
}
// UpdateContainer implements the VC function of the same name.
func (impl *VCImpl) UpdateContainer(ctx context.Context, sandboxID, containerID string, resources specs.LinuxResources) error {
return UpdateContainer(ctx, sandboxID, containerID, resources)
}
// PauseContainer implements the VC function of the same name.
func (impl *VCImpl) PauseContainer(ctx context.Context, sandboxID, containerID string) error {
return PauseContainer(ctx, sandboxID, containerID)
}
// ResumeContainer implements the VC function of the same name.
func (impl *VCImpl) ResumeContainer(ctx context.Context, sandboxID, containerID string) error {
return ResumeContainer(ctx, sandboxID, containerID)
}
// AddDevice will add a device to sandbox
func (impl *VCImpl) AddDevice(ctx context.Context, sandboxID string, info config.DeviceInfo) (api.Device, error) {
return AddDevice(ctx, sandboxID, info)
}
// AddInterface implements the VC function of the same name.
func (impl *VCImpl) AddInterface(ctx context.Context, sandboxID string, inf *vcTypes.Interface) (*vcTypes.Interface, error) {
return AddInterface(ctx, sandboxID, inf)
}
// RemoveInterface implements the VC function of the same name.
func (impl *VCImpl) RemoveInterface(ctx context.Context, sandboxID string, inf *vcTypes.Interface) (*vcTypes.Interface, error) {
return RemoveInterface(ctx, sandboxID, inf)
}
// ListInterfaces implements the VC function of the same name.
func (impl *VCImpl) ListInterfaces(ctx context.Context, sandboxID string) ([]*vcTypes.Interface, error) {
return ListInterfaces(ctx, sandboxID)
}
// UpdateRoutes implements the VC function of the same name.
func (impl *VCImpl) UpdateRoutes(ctx context.Context, sandboxID string, routes []*vcTypes.Route) ([]*vcTypes.Route, error) {
return UpdateRoutes(ctx, sandboxID, routes)
}
// ListRoutes implements the VC function of the same name.
func (impl *VCImpl) ListRoutes(ctx context.Context, sandboxID string) ([]*vcTypes.Route, error) {
return ListRoutes(ctx, sandboxID)
}
// CleanupContaienr is used by shimv2 to stop and delete a container exclusively, once there is no container
// in the sandbox left, do stop the sandbox and delete it. Those serial operations will be done exclusively by
// locking the sandbox.

View File

@ -24,36 +24,6 @@ type VC interface {
SetFactory(ctx context.Context, factory Factory)
CreateSandbox(ctx context.Context, sandboxConfig SandboxConfig) (VCSandbox, error)
DeleteSandbox(ctx context.Context, sandboxID string) (VCSandbox, error)
FetchSandbox(ctx context.Context, sandboxID string) (VCSandbox, error)
ListSandbox(ctx context.Context) ([]SandboxStatus, error)
RunSandbox(ctx context.Context, sandboxConfig SandboxConfig) (VCSandbox, error)
StartSandbox(ctx context.Context, sandboxID string) (VCSandbox, error)
StatusSandbox(ctx context.Context, sandboxID string) (SandboxStatus, error)
StopSandbox(ctx context.Context, sandboxID string, force bool) (VCSandbox, error)
CreateContainer(ctx context.Context, sandboxID string, containerConfig ContainerConfig) (VCSandbox, VCContainer, error)
DeleteContainer(ctx context.Context, sandboxID, containerID string) (VCContainer, error)
EnterContainer(ctx context.Context, sandboxID, containerID string, cmd types.Cmd) (VCSandbox, VCContainer, *Process, error)
KillContainer(ctx context.Context, sandboxID, containerID string, signal syscall.Signal, all bool) error
StartContainer(ctx context.Context, sandboxID, containerID string) (VCContainer, error)
StatusContainer(ctx context.Context, sandboxID, containerID string) (ContainerStatus, error)
StatsContainer(ctx context.Context, sandboxID, containerID string) (ContainerStats, error)
StatsSandbox(ctx context.Context, sandboxID string) (SandboxStats, []ContainerStats, error)
StopContainer(ctx context.Context, sandboxID, containerID string) (VCContainer, error)
ProcessListContainer(ctx context.Context, sandboxID, containerID string, options ProcessListOptions) (ProcessList, error)
UpdateContainer(ctx context.Context, sandboxID, containerID string, resources specs.LinuxResources) error
PauseContainer(ctx context.Context, sandboxID, containerID string) error
ResumeContainer(ctx context.Context, sandboxID, containerID string) error
AddDevice(ctx context.Context, sandboxID string, info config.DeviceInfo) (api.Device, error)
AddInterface(ctx context.Context, sandboxID string, inf *vcTypes.Interface) (*vcTypes.Interface, error)
RemoveInterface(ctx context.Context, sandboxID string, inf *vcTypes.Interface) (*vcTypes.Interface, error)
ListInterfaces(ctx context.Context, sandboxID string) ([]*vcTypes.Interface, error)
UpdateRoutes(ctx context.Context, sandboxID string, routes []*vcTypes.Route) ([]*vcTypes.Route, error)
ListRoutes(ctx context.Context, sandboxID string) ([]*vcTypes.Route, error)
CleanupContainer(ctx context.Context, sandboxID, containerID string, force bool) error
}

View File

@ -6,6 +6,7 @@
package vcmock
import (
"fmt"
"io"
"syscall"
@ -95,7 +96,10 @@ func (s *Sandbox) Delete() error {
// CreateContainer implements the VCSandbox function of the same name.
func (s *Sandbox) CreateContainer(conf vc.ContainerConfig) (vc.VCContainer, error) {
return &Container{}, nil
if s.CreateContainerFunc != nil {
return s.CreateContainerFunc(conf)
}
return nil, fmt.Errorf("%s: %s (%+v): sandboxID: %v, containerConfig: %v", mockErrorPrefix, getSelf(), s, s.MockID, conf)
}
// DeleteContainer implements the VCSandbox function of the same name.

View File

@ -24,5 +24,8 @@ func getSelf() string {
// IsMockError returns true if the specified error was generated by this
// package.
func IsMockError(err error) bool {
if err == nil {
return false
}
return strings.HasPrefix(err.Error(), mockErrorPrefix)
}