mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-04-29 12:14:48 +00:00
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:
parent
e3a3818f7a
commit
069505e2d5
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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())
|
||||
}
|
@ -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")
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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(),
|
||||
},
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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())
|
||||
}
|
@ -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))
|
||||
}
|
@ -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
|
||||
},
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
},
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
@ -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
@ -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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user