From 069505e2d5eeeb09351f6dc3bdf63672a326112c Mon Sep 17 00:00:00 2001 From: bin liu Date: Mon, 1 Jun 2020 03:45:18 +0000 Subject: [PATCH] runtime: delete unused sub-commands. This PR delete codes not used anymore. Fixes: #332 Signed-off-by: bin liu --- src/runtime/cli/create.go | 185 --- src/runtime/cli/create_test.go | 724 -------- src/runtime/cli/delete.go | 189 --- src/runtime/cli/delete_test.go | 626 ------- src/runtime/cli/events.go | 295 ---- src/runtime/cli/events_test.go | 144 -- src/runtime/cli/exec.go | 291 ---- src/runtime/cli/exec_test.go | 787 --------- src/runtime/cli/kata-overhead.go | 136 -- src/runtime/cli/kill.go | 193 --- src/runtime/cli/kill_test.go | 431 ----- src/runtime/cli/list.go | 394 ----- src/runtime/cli/list_test.go | 785 --------- src/runtime/cli/main.go | 16 - src/runtime/cli/main_test.go | 21 +- src/runtime/cli/network.go | 229 --- src/runtime/cli/network_test.go | 92 -- src/runtime/cli/oci.go | 213 --- src/runtime/cli/oci_test.go | 319 ---- src/runtime/cli/pause.go | 93 -- src/runtime/cli/pause_test.go | 175 -- src/runtime/cli/ps.go | 106 -- src/runtime/cli/ps_test.go | 120 -- src/runtime/cli/run.go | 129 -- src/runtime/cli/run_test.go | 670 -------- src/runtime/cli/spec.go | 114 -- src/runtime/cli/spec_test.go | 35 - src/runtime/cli/start.go | 113 -- src/runtime/cli/start_test.go | 243 --- src/runtime/cli/state.go | 70 - src/runtime/cli/state_test.go | 85 - src/runtime/cli/update.go | 288 ---- src/runtime/cli/update_test.go | 208 --- src/runtime/containerd-shim-v2/create.go | 4 +- src/runtime/containerd-shim-v2/create_test.go | 11 +- src/runtime/pkg/katautils/create.go | 34 +- src/runtime/pkg/katautils/create_test.go | 48 +- src/runtime/pkg/katautils/oci.go | 110 -- src/runtime/pkg/katautils/oci_test.go | 140 -- src/runtime/virtcontainers/api_test.go | 1453 +---------------- .../documentation/api/1.0/api.md | 12 +- .../virtcontainers/example_pod_run_test.go | 12 +- src/runtime/virtcontainers/implementation.go | 137 -- src/runtime/virtcontainers/interfaces.go | 30 - .../virtcontainers/pkg/vcmock/sandbox.go | 6 +- .../virtcontainers/pkg/vcmock/utils.go | 3 + 46 files changed, 66 insertions(+), 10453 deletions(-) delete mode 100644 src/runtime/cli/create.go delete mode 100644 src/runtime/cli/create_test.go delete mode 100644 src/runtime/cli/delete.go delete mode 100644 src/runtime/cli/delete_test.go delete mode 100644 src/runtime/cli/events.go delete mode 100644 src/runtime/cli/events_test.go delete mode 100644 src/runtime/cli/exec.go delete mode 100644 src/runtime/cli/exec_test.go delete mode 100644 src/runtime/cli/kata-overhead.go delete mode 100644 src/runtime/cli/kill.go delete mode 100644 src/runtime/cli/kill_test.go delete mode 100644 src/runtime/cli/list.go delete mode 100644 src/runtime/cli/list_test.go delete mode 100644 src/runtime/cli/network.go delete mode 100644 src/runtime/cli/network_test.go delete mode 100644 src/runtime/cli/oci.go delete mode 100644 src/runtime/cli/oci_test.go delete mode 100644 src/runtime/cli/pause.go delete mode 100644 src/runtime/cli/pause_test.go delete mode 100644 src/runtime/cli/ps.go delete mode 100644 src/runtime/cli/ps_test.go delete mode 100644 src/runtime/cli/run.go delete mode 100644 src/runtime/cli/run_test.go delete mode 100644 src/runtime/cli/spec.go delete mode 100644 src/runtime/cli/spec_test.go delete mode 100644 src/runtime/cli/start.go delete mode 100644 src/runtime/cli/start_test.go delete mode 100644 src/runtime/cli/state.go delete mode 100644 src/runtime/cli/state_test.go delete mode 100644 src/runtime/cli/update.go delete mode 100644 src/runtime/cli/update_test.go delete mode 100644 src/runtime/pkg/katautils/oci.go delete mode 100644 src/runtime/pkg/katautils/oci_test.go diff --git a/src/runtime/cli/create.go b/src/runtime/cli/create.go deleted file mode 100644 index 11a0e0effa..0000000000 --- a/src/runtime/cli/create.go +++ /dev/null @@ -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: ` - - 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 -} diff --git a/src/runtime/cli/create_test.go b/src/runtime/cli/create_test.go deleted file mode 100644 index c4d2113bec..0000000000 --- a/src/runtime/cli/create_test.go +++ /dev/null @@ -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) - } -} diff --git a/src/runtime/cli/delete.go b/src/runtime/cli/delete.go deleted file mode 100644 index 904b92e819..0000000000 --- a/src/runtime/cli/delete.go +++ /dev/null @@ -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...] - - 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 -} diff --git a/src/runtime/cli/delete_test.go b/src/runtime/cli/delete_test.go deleted file mode 100644 index b7e480287d..0000000000 --- a/src/runtime/cli/delete_test.go +++ /dev/null @@ -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) -} diff --git a/src/runtime/cli/events.go b/src/runtime/cli/events.go deleted file mode 100644 index 6623bc776a..0000000000 --- a/src/runtime/cli/events.go +++ /dev/null @@ -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: ` - -Where "" 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 -} diff --git a/src/runtime/cli/events_test.go b/src/runtime/cli/events_test.go deleted file mode 100644 index 8c437e6293..0000000000 --- a/src/runtime/cli/events_test.go +++ /dev/null @@ -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) -} diff --git a/src/runtime/cli/exec.go b/src/runtime/cli/exec.go deleted file mode 100644 index 46149be185..0000000000 --- a/src/runtime/cli/exec.go +++ /dev/null @@ -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: ` [command options] || -p process.json - - is the name for the instance of the container and - is the command to be executed in the container. 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 + ` 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: [:])", - }, - 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()) -} diff --git a/src/runtime/cli/exec_test.go b/src/runtime/cli/exec_test.go deleted file mode 100644 index 23bd1c4e71..0000000000 --- a/src/runtime/cli/exec_test.go +++ /dev/null @@ -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") -} diff --git a/src/runtime/cli/kata-overhead.go b/src/runtime/cli/kata-overhead.go deleted file mode 100644 index c6088f0d2b..0000000000 --- a/src/runtime/cli/kata-overhead.go +++ /dev/null @@ -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...] - - 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 -} diff --git a/src/runtime/cli/kill.go b/src/runtime/cli/kill.go deleted file mode 100644 index 52dc35e7dd..0000000000 --- a/src/runtime/cli/kill.go +++ /dev/null @@ -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: ` [signal] - - 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) -} diff --git a/src/runtime/cli/kill_test.go b/src/runtime/cli/kill_test.go deleted file mode 100644 index 52fa17fed4..0000000000 --- a/src/runtime/cli/kill_test.go +++ /dev/null @@ -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) -} diff --git a/src/runtime/cli/list.go b/src/runtime/cli/list.go deleted file mode 100644 index d485fd0db0..0000000000 --- a/src/runtime/cli/list.go +++ /dev/null @@ -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(), - }, - } -} diff --git a/src/runtime/cli/list_test.go b/src/runtime/cli/list_test.go deleted file mode 100644 index eb3bdc25a6..0000000000 --- a/src/runtime/cli/list_test.go +++ /dev/null @@ -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) -} diff --git a/src/runtime/cli/main.go b/src/runtime/cli/main.go index 7a11efe2b0..a8b3c42c2a 100644 --- a/src/runtime/cli/main.go +++ b/src/runtime/cli/main.go @@ -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, } diff --git a/src/runtime/cli/main_test.go b/src/runtime/cli/main_test.go index 58374fa800..072918d3e0 100644 --- a/src/runtime/cli/main_test.go +++ b/src/runtime/cli/main_test.go @@ -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 -} diff --git a/src/runtime/cli/network.go b/src/runtime/cli/network.go deleted file mode 100644 index c6a871b56f..0000000000 --- a/src/runtime/cli/network.go +++ /dev/null @@ -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 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 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 `, - 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 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 `, - 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 -} diff --git a/src/runtime/cli/network_test.go b/src/runtime/cli/network_test.go deleted file mode 100644 index f8027cf373..0000000000 --- a/src/runtime/cli/network_test.go +++ /dev/null @@ -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) -} diff --git a/src/runtime/cli/oci.go b/src/runtime/cli/oci.go deleted file mode 100644 index 5eb10dfcc5..0000000000 --- a/src/runtime/cli/oci.go +++ /dev/null @@ -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 -} diff --git a/src/runtime/cli/oci_test.go b/src/runtime/cli/oci_test.go deleted file mode 100644 index 6a3e60cf6e..0000000000 --- a/src/runtime/cli/oci_test.go +++ /dev/null @@ -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) - } -} diff --git a/src/runtime/cli/pause.go b/src/runtime/cli/pause.go deleted file mode 100644 index 17ca3c55c8..0000000000 --- a/src/runtime/cli/pause.go +++ /dev/null @@ -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: ` - -Where "" 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: ` - -Where "" 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 -} diff --git a/src/runtime/cli/pause_test.go b/src/runtime/cli/pause_test.go deleted file mode 100644 index ea77eaf839..0000000000 --- a/src/runtime/cli/pause_test.go +++ /dev/null @@ -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) -} diff --git a/src/runtime/cli/ps.go b/src/runtime/cli/ps.go deleted file mode 100644 index 037f2e5923..0000000000 --- a/src/runtime/cli/ps.go +++ /dev/null @@ -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: ` [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 -} diff --git a/src/runtime/cli/ps_test.go b/src/runtime/cli/ps_test.go deleted file mode 100644 index 872c3aee7b..0000000000 --- a/src/runtime/cli/ps_test.go +++ /dev/null @@ -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) -} diff --git a/src/runtime/cli/run.go b/src/runtime/cli/run.go deleted file mode 100644 index c08fec1f07..0000000000 --- a/src/runtime/cli/run.go +++ /dev/null @@ -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: ` - - 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()) -} diff --git a/src/runtime/cli/run_test.go b/src/runtime/cli/run_test.go deleted file mode 100644 index 2648c1a2fe..0000000000 --- a/src/runtime/cli/run_test.go +++ /dev/null @@ -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)) -} diff --git a/src/runtime/cli/spec.go b/src/runtime/cli/spec.go deleted file mode 100644 index 518ef08b87..0000000000 --- a/src/runtime/cli/spec.go +++ /dev/null @@ -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 - }, -} diff --git a/src/runtime/cli/spec_test.go b/src/runtime/cli/spec_test.go deleted file mode 100644 index 6ebf5bdc50..0000000000 --- a/src/runtime/cli/spec_test.go +++ /dev/null @@ -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) -} diff --git a/src/runtime/cli/start.go b/src/runtime/cli/start.go deleted file mode 100644 index b78f76abd0..0000000000 --- a/src/runtime/cli/start.go +++ /dev/null @@ -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...] - - 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 -} diff --git a/src/runtime/cli/start_test.go b/src/runtime/cli/start_test.go deleted file mode 100644 index ddd068c70e..0000000000 --- a/src/runtime/cli/start_test.go +++ /dev/null @@ -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) -} diff --git a/src/runtime/cli/state.go b/src/runtime/cli/state.go deleted file mode 100644 index 36dbdcaea1..0000000000 --- a/src/runtime/cli/state.go +++ /dev/null @@ -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: ` - - 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 -} diff --git a/src/runtime/cli/state_test.go b/src/runtime/cli/state_test.go deleted file mode 100644 index fd1076aa50..0000000000 --- a/src/runtime/cli/state_test.go +++ /dev/null @@ -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) -} diff --git a/src/runtime/cli/update.go b/src/runtime/cli/update.go deleted file mode 100644 index 350ff91944..0000000000 --- a/src/runtime/cli/update.go +++ /dev/null @@ -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: ``, - 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) - }, -} diff --git a/src/runtime/cli/update_test.go b/src/runtime/cli/update_test.go deleted file mode 100644 index f62bb3b6b7..0000000000 --- a/src/runtime/cli/update_test.go +++ /dev/null @@ -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) -} diff --git a/src/runtime/containerd-shim-v2/create.go b/src/runtime/containerd-shim-v2/create.go index 83c4280507..8cec1676fb 100644 --- a/src/runtime/containerd-shim-v2/create.go +++ b/src/runtime/containerd-shim-v2/create.go @@ -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 } diff --git a/src/runtime/containerd-shim-v2/create_test.go b/src/runtime/containerd-shim-v2/create_test.go index 91b80055ad..3835ad00d5 100644 --- a/src/runtime/containerd-shim-v2/create_test.go +++ b/src/runtime/containerd-shim-v2/create_test.go @@ -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) diff --git a/src/runtime/pkg/katautils/create.go b/src/runtime/pkg/katautils/create.go index be9cbfce8d..ef9ca3ab8d 100644 --- a/src/runtime/pkg/katautils/create.go +++ b/src/runtime/pkg/katautils/create.go @@ -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. diff --git a/src/runtime/pkg/katautils/create_test.go b/src/runtime/pkg/katautils/create_test.go index 69c89dbc8b..866b397229 100644 --- a/src/runtime/pkg/katautils/create_test.go +++ b/src/runtime/pkg/katautils/create_test.go @@ -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) } } diff --git a/src/runtime/pkg/katautils/oci.go b/src/runtime/pkg/katautils/oci.go deleted file mode 100644 index 5c6c5f083e..0000000000 --- a/src/runtime/pkg/katautils/oci.go +++ /dev/null @@ -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) -} diff --git a/src/runtime/pkg/katautils/oci_test.go b/src/runtime/pkg/katautils/oci_test.go deleted file mode 100644 index 4544e353a9..0000000000 --- a/src/runtime/pkg/katautils/oci_test.go +++ /dev/null @@ -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)) -} diff --git a/src/runtime/virtcontainers/api_test.go b/src/runtime/virtcontainers/api_test.go index 5f63d4db92..b3f176d5a5 100644 --- a/src/runtime/virtcontainers/api_test.go +++ b/src/runtime/virtcontainers/api_test.go @@ -12,7 +12,6 @@ import ( "os" "path/filepath" "strings" - "syscall" "testing" ktu "github.com/kata-containers/kata-containers/src/runtime/pkg/katatestutils" @@ -21,7 +20,6 @@ import ( vccgroups "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/cgroups" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/mock" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/rootless" - 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/stretchr/testify/assert" @@ -215,1213 +213,6 @@ func TestCreateSandboxFailing(t *testing.T) { assert.Nil(p.(*Sandbox)) } -func TestDeleteSandboxNoopAgentSuccessful(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - config := newTestSandboxConfigNoop() - - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - s, ok := p.(*Sandbox) - assert.True(ok) - sandboxDir := filepath.Join(s.newStore.RunStoragePath(), p.ID()) - _, err = os.Stat(sandboxDir) - assert.NoError(err) - - p, err = DeleteSandbox(ctx, p.ID()) - assert.NoError(err) - assert.NotNil(p) - - _, err = os.Stat(sandboxDir) - assert.Error(err) -} - -func TestDeleteSandboxKataAgentSuccessful(t *testing.T) { - assert := assert.New(t) - if tc.NotValid(ktu.NeedRoot()) { - t.Skip(testDisabledAsNonRoot) - } - - defer cleanUp() - - config := newTestSandboxConfigKataAgent() - - sockDir, err := testGenerateKataProxySockDir() - assert.NoError(err) - - defer os.RemoveAll(sockDir) - - testKataProxyURL := fmt.Sprintf(testKataProxyURLTempl, sockDir) - noopProxyURL = testKataProxyURL - - impl := &gRPCProxy{} - - kataProxyMock := mock.ProxyGRPCMock{ - GRPCImplementer: impl, - GRPCRegister: gRPCRegister, - } - err = kataProxyMock.Start(testKataProxyURL) - assert.NoError(err) - defer kataProxyMock.Stop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - s, ok := p.(*Sandbox) - assert.True(ok) - sandboxDir := filepath.Join(s.newStore.RunStoragePath(), p.ID()) - _, err = os.Stat(sandboxDir) - assert.NoError(err) - - p, err = DeleteSandbox(ctx, p.ID()) - assert.NoError(err) - assert.NotNil(p) - - _, err = os.Stat(sandboxDir) - assert.Error(err) -} - -func TestDeleteSandboxFailing(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := DeleteSandbox(ctx, testSandboxID) - assert.Error(err) - assert.Nil(p) -} - -func TestStartSandboxNoopAgentSuccessful(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, _, err := createAndStartSandbox(ctx, config) - assert.NoError(err) - assert.NotNil(p) -} - -func TestStartSandboxKataAgentSuccessful(t *testing.T) { - assert := assert.New(t) - if tc.NotValid(ktu.NeedRoot()) { - t.Skip(testDisabledAsNonRoot) - } - - defer cleanUp() - - config := newTestSandboxConfigKataAgent() - - sockDir, err := testGenerateKataProxySockDir() - assert.NoError(err) - defer os.RemoveAll(sockDir) - - testKataProxyURL := fmt.Sprintf(testKataProxyURLTempl, sockDir) - noopProxyURL = testKataProxyURL - - impl := &gRPCProxy{} - - kataProxyMock := mock.ProxyGRPCMock{ - GRPCImplementer: impl, - GRPCRegister: gRPCRegister, - } - err = kataProxyMock.Start(testKataProxyURL) - assert.NoError(err) - defer kataProxyMock.Stop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, _, err := createAndStartSandbox(ctx, config) - assert.NoError(err) - assert.NotNil(p) - - pImpl, ok := p.(*Sandbox) - assert.True(ok) - - // TODO: defaultSharedDir is a hyper var = /run/hyper/shared/sandboxes - // do we need to unmount sandboxes and containers? - err = bindUnmountAllRootfs(ctx, filepath.Join(testDir, p.ID()), pImpl) - assert.NoError(err) -} - -func TestStartSandboxFailing(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := StartSandbox(ctx, testSandboxID) - assert.Error(err) - assert.Nil(p) -} - -func TestStopSandboxNoopAgentSuccessful(t *testing.T) { - if tc.NotValid(ktu.NeedRoot()) { - t.Skip(testDisabledAsNonRoot) - } - defer cleanUp() - assert := assert.New(t) - - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, _, err := createAndStartSandbox(ctx, config) - assert.NoError(err) - assert.NotNil(p) - - vp, err := StopSandbox(ctx, p.ID(), false) - assert.NoError(err) - assert.NotNil(vp) -} - -func TestStopSandboxKataAgentSuccessful(t *testing.T) { - assert := assert.New(t) - if tc.NotValid(ktu.NeedRoot()) { - t.Skip(testDisabledAsNonRoot) - } - - defer cleanUp() - - config := newTestSandboxConfigKataAgent() - - sockDir, err := testGenerateKataProxySockDir() - assert.NoError(err) - defer os.RemoveAll(sockDir) - - testKataProxyURL := fmt.Sprintf(testKataProxyURLTempl, sockDir) - noopProxyURL = testKataProxyURL - - impl := &gRPCProxy{} - - kataProxyMock := mock.ProxyGRPCMock{ - GRPCImplementer: impl, - GRPCRegister: gRPCRegister, - } - err = kataProxyMock.Start(testKataProxyURL) - assert.NoError(err) - defer kataProxyMock.Stop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, _, err := createAndStartSandbox(ctx, config) - assert.NoError(err) - assert.NotNil(p) - - p, err = StopSandbox(ctx, p.ID(), false) - assert.NoError(err) - assert.NotNil(p) -} - -func TestStopSandboxFailing(t *testing.T) { - defer cleanUp() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := StopSandbox(ctx, testSandboxID, false) - assert.Error(t, err) - assert.Nil(t, p) -} - -func TestRunSandboxNoopAgentSuccessful(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := RunSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - s, ok := p.(*Sandbox) - assert.True(ok) - sandboxDir := filepath.Join(s.newStore.RunStoragePath(), p.ID()) - _, err = os.Stat(sandboxDir) - assert.NoError(err) -} - -func TestRunSandboxKataAgentSuccessful(t *testing.T) { - assert := assert.New(t) - if tc.NotValid(ktu.NeedRoot()) { - t.Skip(testDisabledAsNonRoot) - } - - defer cleanUp() - - config := newTestSandboxConfigKataAgent() - - sockDir, err := testGenerateKataProxySockDir() - assert.NoError(err) - - defer os.RemoveAll(sockDir) - - testKataProxyURL := fmt.Sprintf(testKataProxyURLTempl, sockDir) - noopProxyURL = testKataProxyURL - - impl := &gRPCProxy{} - - kataProxyMock := mock.ProxyGRPCMock{ - GRPCImplementer: impl, - GRPCRegister: gRPCRegister, - } - err = kataProxyMock.Start(testKataProxyURL) - assert.NoError(err) - defer kataProxyMock.Stop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := RunSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - pImpl, ok := p.(*Sandbox) - assert.True(ok) - - sandboxDir := filepath.Join(pImpl.newStore.RunStoragePath(), p.ID()) - _, err = os.Stat(sandboxDir) - assert.NoError(err) - - err = bindUnmountAllRootfs(ctx, filepath.Join(testDir, p.ID()), pImpl) - assert.NoError(err) -} - -func TestRunSandboxFailing(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - config := SandboxConfig{} - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := RunSandbox(ctx, config, nil) - assert.Error(err) - assert.Nil(p) -} - -func TestListSandboxSuccessful(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - _, err = ListSandbox(ctx) - assert.NoError(err) -} - -func TestListSandboxNoSandboxDirectory(t *testing.T) { - defer cleanUp() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - _, err := ListSandbox(ctx) - assert.NoError(t, err) -} - -func TestStatusSandboxSuccessfulStateReady(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - config := newTestSandboxConfigNoop() - cgroupPath, err := vccgroups.RenameCgroupPath(vccgroups.DefaultCgroupPath) - assert.NoError(err) - - hypervisorConfig := HypervisorConfig{ - KernelPath: filepath.Join(testDir, testKernel), - ImagePath: filepath.Join(testDir, testImage), - HypervisorPath: filepath.Join(testDir, testHypervisor), - NumVCPUs: defaultVCPUs, - MemorySize: defaultMemSzMiB, - DefaultBridges: defaultBridges, - BlockDeviceDriver: defaultBlockDriver, - DefaultMaxVCPUs: defaultMaxQemuVCPUs, - Msize9p: defaultMsize9p, - } - - expectedStatus := SandboxStatus{ - ID: testSandboxID, - State: types.SandboxState{ - State: types.StateReady, - BlockIndexMap: make(map[int]struct{}), - PersistVersion: 2, - }, - Hypervisor: MockHypervisor, - HypervisorConfig: hypervisorConfig, - ContainersStatus: []ContainerStatus{ - { - ID: containerID, - State: types.ContainerState{ - State: types.StateReady, - CgroupPath: cgroupPath, - }, - PID: 0, - RootFs: filepath.Join(testDir, testBundle), - Annotations: containerAnnotations, - Spec: newEmptySpec(), - }, - }, - } - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - status, err := StatusSandbox(ctx, p.ID()) - assert.NoError(err) - - // Copy the start time as we can't pretend we know what that - // value will be. - expectedStatus.ContainersStatus[0].StartTime = status.ContainersStatus[0].StartTime - - assert.Equal(expectedStatus, status) -} - -func TestStatusSandboxSuccessfulStateRunning(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - config := newTestSandboxConfigNoop() - cgroupPath, err := vccgroups.RenameCgroupPath(vccgroups.DefaultCgroupPath) - assert.NoError(err) - - hypervisorConfig := HypervisorConfig{ - KernelPath: filepath.Join(testDir, testKernel), - ImagePath: filepath.Join(testDir, testImage), - HypervisorPath: filepath.Join(testDir, testHypervisor), - NumVCPUs: defaultVCPUs, - MemorySize: defaultMemSzMiB, - DefaultBridges: defaultBridges, - BlockDeviceDriver: defaultBlockDriver, - DefaultMaxVCPUs: defaultMaxQemuVCPUs, - Msize9p: defaultMsize9p, - } - - expectedStatus := SandboxStatus{ - ID: testSandboxID, - State: types.SandboxState{ - State: types.StateRunning, - BlockIndexMap: make(map[int]struct{}), - PersistVersion: 2, - }, - Hypervisor: MockHypervisor, - HypervisorConfig: hypervisorConfig, - ContainersStatus: []ContainerStatus{ - { - ID: containerID, - State: types.ContainerState{ - State: types.StateRunning, - CgroupPath: cgroupPath, - }, - PID: 0, - RootFs: filepath.Join(testDir, testBundle), - Annotations: containerAnnotations, - Spec: newEmptySpec(), - }, - }, - } - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - p, err = StartSandbox(ctx, p.ID()) - assert.NoError(err) - assert.NotNil(p) - - status, err := StatusSandbox(ctx, p.ID()) - assert.NoError(err) - - // Copy the start time as we can't pretend we know what that - // value will be. - expectedStatus.ContainersStatus[0].StartTime = status.ContainersStatus[0].StartTime - - assert.Exactly(expectedStatus, status) -} - -func TestStatusSandboxFailingFetchSandboxConfig(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - rmSandboxDir(p.ID()) - globalSandboxList.removeSandbox(p.ID()) - - _, err = StatusSandbox(ctx, p.ID()) - assert.Error(err) -} - -func TestStatusPodSandboxFailingFetchSandboxState(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - rmSandboxDir(p.ID()) - globalSandboxList.removeSandbox(p.ID()) - - _, err = StatusSandbox(ctx, p.ID()) - assert.Error(err) -} - -func newTestContainerConfigNoop(contID string) ContainerConfig { - // Define the container command and bundle. - container := ContainerConfig{ - ID: contID, - RootFs: RootFs{Target: filepath.Join(testDir, testBundle), Mounted: true}, - Cmd: newBasicTestCmd(), - Annotations: containerAnnotations, - CustomSpec: newEmptySpec(), - } - - return container -} - -func TestCreateContainerSuccessful(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - contID := "100" - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - s, ok := p.(*Sandbox) - assert.True(ok) - sandboxDir := filepath.Join(s.newStore.RunStoragePath(), p.ID()) - _, err = os.Stat(sandboxDir) - assert.NoError(err) - - contConfig := newTestContainerConfigNoop(contID) - - _, c, err := CreateContainer(ctx, p.ID(), contConfig) - assert.NoError(err) - assert.NotNil(c) - - contDir := filepath.Join(sandboxDir, contID) - _, err = os.Stat(contDir) - assert.NoError(err) -} - -func TestCreateContainerFailingNoSandbox(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - contID := "100" - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - p, err = DeleteSandbox(ctx, p.ID()) - assert.NoError(err) - assert.NotNil(p) - - s, ok := p.(*Sandbox) - assert.True(ok) - sandboxDir := filepath.Join(s.newStore.RunStoragePath(), p.ID()) - _, err = os.Stat(sandboxDir) - assert.Error(err) - - contConfig := newTestContainerConfigNoop(contID) - - _, c, err := CreateContainer(ctx, p.ID(), contConfig) - assert.Error(err) - assert.Nil(c) -} - -func TestDeleteContainerSuccessful(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - contID := "100" - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - s, ok := p.(*Sandbox) - assert.True(ok) - sandboxDir := filepath.Join(s.newStore.RunStoragePath(), p.ID()) - _, err = os.Stat(sandboxDir) - assert.NoError(err) - - contConfig := newTestContainerConfigNoop(contID) - - _, c, err := CreateContainer(ctx, p.ID(), contConfig) - assert.NoError(err) - assert.NotNil(c) - - contDir := filepath.Join(sandboxDir, contID) - _, err = os.Stat(contDir) - assert.NoError(err) - - c, err = DeleteContainer(ctx, p.ID(), contID) - assert.NoError(err) - assert.NotNil(c) - - _, err = os.Stat(contDir) - assert.Error(err) -} - -func TestDeleteContainerFailingNoSandbox(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - contID := "100" - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - c, err := DeleteContainer(ctx, testSandboxID, contID) - assert.Error(err) - assert.Nil(c) -} - -func TestDeleteContainerFailingNoContainer(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - contID := "100" - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - s, ok := p.(*Sandbox) - assert.True(ok) - sandboxDir := filepath.Join(s.newStore.RunStoragePath(), p.ID()) - _, err = os.Stat(sandboxDir) - assert.NoError(err) - - c, err := DeleteContainer(ctx, p.ID(), contID) - assert.Error(err) - assert.Nil(c) -} - -func TestStartContainerNoopAgentSuccessful(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - contID := "100" - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - - p, sandboxDir, err := createAndStartSandbox(ctx, config) - assert.NoError(err) - assert.NotNil(p) - contConfig := newTestContainerConfigNoop(contID) - - _, c, err := CreateContainer(ctx, p.ID(), contConfig) - assert.NoError(err) - assert.NotNil(c) - - contDir := filepath.Join(sandboxDir, contID) - _, err = os.Stat(contDir) - assert.NoError(err) - - c, err = StartContainer(ctx, p.ID(), contID) - assert.NoError(err) - assert.NotNil(c) -} - -func TestStartContainerFailingNoSandbox(t *testing.T) { - defer cleanUp() - - contID := "100" - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - c, err := StartContainer(ctx, testSandboxID, contID) - assert.Error(t, err) - assert.Nil(t, c) -} - -func TestStartContainerFailingNoContainer(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - contID := "100" - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - s, ok := p.(*Sandbox) - assert.True(ok) - sandboxDir := filepath.Join(s.newStore.RunStoragePath(), p.ID()) - _, err = os.Stat(sandboxDir) - assert.NoError(err) - - c, err := StartContainer(ctx, p.ID(), contID) - assert.Error(err) - assert.Nil(c) -} - -func TestStartContainerFailingSandboxNotStarted(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - contID := "100" - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - s, ok := p.(*Sandbox) - assert.True(ok) - sandboxDir := filepath.Join(s.newStore.RunStoragePath(), p.ID()) - _, err = os.Stat(sandboxDir) - assert.NoError(err) - - contConfig := newTestContainerConfigNoop(contID) - - _, c, err := CreateContainer(ctx, p.ID(), contConfig) - assert.NoError(err) - assert.NotNil(c) - - contDir := filepath.Join(sandboxDir, contID) - _, err = os.Stat(contDir) - assert.NoError(err) - - _, err = StartContainer(ctx, p.ID(), contID) - assert.Error(err) -} - -func TestStopContainerNoopAgentSuccessful(t *testing.T) { - if tc.NotValid(ktu.NeedRoot()) { - t.Skip(testDisabledAsNonRoot) - } - defer cleanUp() - assert := assert.New(t) - - contID := "100" - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - - p, sandboxDir, err := createAndStartSandbox(ctx, config) - assert.NoError(err) - assert.NotNil(p) - - contConfig := newTestContainerConfigNoop(contID) - - _, c, err := CreateContainer(ctx, p.ID(), contConfig) - assert.NoError(err) - assert.NotNil(c) - - contDir := filepath.Join(sandboxDir, contID) - _, err = os.Stat(contDir) - assert.NoError(err) - - c, err = StartContainer(ctx, p.ID(), contID) - assert.NoError(err) - assert.NotNil(c) - - c, err = StopContainer(ctx, p.ID(), contID) - assert.NoError(err) - assert.NotNil(c) -} - -func TestStopContainerFailingNoSandbox(t *testing.T) { - if tc.NotValid(ktu.NeedRoot()) { - t.Skip(testDisabledAsNonRoot) - } - defer cleanUp() - - contID := "100" - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - c, err := StopContainer(ctx, testSandboxID, contID) - assert.Error(t, err) - assert.Nil(t, c) -} - -func TestStopContainerFailingNoContainer(t *testing.T) { - if tc.NotValid(ktu.NeedRoot()) { - t.Skip(testDisabledAsNonRoot) - } - defer cleanUp() - assert := assert.New(t) - - contID := "100" - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - s, ok := p.(*Sandbox) - assert.True(ok) - sandboxDir := filepath.Join(s.newStore.RunStoragePath(), p.ID()) - _, err = os.Stat(sandboxDir) - assert.NoError(err) - - c, err := StopContainer(ctx, p.ID(), contID) - assert.Error(err) - assert.Nil(c) -} - -func testKillContainerFromContReadySuccessful(t *testing.T, signal syscall.Signal) { - defer cleanUp() - assert := assert.New(t) - - contID := "100" - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - - p, sandboxDir, err := createAndStartSandbox(ctx, config) - assert.NoError(err) - assert.NotNil(p) - - contConfig := newTestContainerConfigNoop(contID) - - _, c, err := CreateContainer(ctx, p.ID(), contConfig) - assert.NoError(err) - assert.NotNil(c) - - contDir := filepath.Join(sandboxDir, contID) - _, err = os.Stat(contDir) - assert.NoError(err) - - err = KillContainer(ctx, p.ID(), contID, signal, false) - assert.NoError(err) -} - -func TestKillContainerFromContReadySuccessful(t *testing.T) { - // SIGUSR1 - testKillContainerFromContReadySuccessful(t, syscall.SIGUSR1) - // SIGUSR2 - testKillContainerFromContReadySuccessful(t, syscall.SIGUSR2) - // SIGKILL - testKillContainerFromContReadySuccessful(t, syscall.SIGKILL) - // SIGTERM - testKillContainerFromContReadySuccessful(t, syscall.SIGTERM) -} - -func TestEnterContainerNoopAgentSuccessful(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - contID := "100" - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - - p, sandboxDir, err := createAndStartSandbox(ctx, config) - assert.NoError(err) - assert.NotNil(p) - - contConfig := newTestContainerConfigNoop(contID) - - _, c, err := CreateContainer(ctx, p.ID(), contConfig) - assert.NoError(err) - assert.NotNil(c) - - contDir := filepath.Join(sandboxDir, contID) - _, err = os.Stat(contDir) - assert.NoError(err) - - c, err = StartContainer(ctx, p.ID(), contID) - assert.NoError(err) - assert.NotNil(c) - - cmd := newBasicTestCmd() - - _, c, _, err = EnterContainer(ctx, p.ID(), contID, cmd) - assert.NoError(err) - assert.NotNil(c) -} - -func TestEnterContainerFailingNoSandbox(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - contID := "100" - cmd := newBasicTestCmd() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - _, c, _, err := EnterContainer(ctx, testSandboxID, contID, cmd) - assert.Error(err) - assert.Nil(c) -} - -func TestEnterContainerFailingNoContainer(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - contID := "100" - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - s, ok := p.(*Sandbox) - assert.True(ok) - sandboxDir := filepath.Join(s.newStore.RunStoragePath(), p.ID()) - _, err = os.Stat(sandboxDir) - assert.NoError(err) - - cmd := newBasicTestCmd() - - _, c, _, err := EnterContainer(ctx, p.ID(), contID, cmd) - assert.Error(err) - assert.Nil(c) -} - -func TestEnterContainerFailingContNotStarted(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - contID := "100" - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - - p, sandboxDir, err := createAndStartSandbox(ctx, config) - assert.NoError(err) - assert.NotNil(p) - - contConfig := newTestContainerConfigNoop(contID) - - _, c, err := CreateContainer(ctx, p.ID(), contConfig) - assert.NoError(err) - assert.NotNil(c) - - contDir := filepath.Join(sandboxDir, contID) - _, err = os.Stat(contDir) - assert.NoError(err) - - cmd := newBasicTestCmd() - - _, c, _, err = EnterContainer(ctx, p.ID(), contID, cmd) - assert.NoError(err) - assert.NotNil(c) -} - -func TestStatusContainerSuccessful(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - contID := "100" - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - pImpl, ok := p.(*Sandbox) - assert.True(ok) - sandboxDir := filepath.Join(pImpl.newStore.RunStoragePath(), p.ID()) - _, err = os.Stat(sandboxDir) - assert.NoError(err) - - contConfig := newTestContainerConfigNoop(contID) - - _, c, err := CreateContainer(ctx, p.ID(), contConfig) - assert.NoError(err) - assert.NotNil(c) - - contDir := filepath.Join(sandboxDir, contID) - _, err = os.Stat(contDir) - assert.NoError(err) - - status, err := StatusContainer(ctx, p.ID(), contID) - assert.NoError(err) - - cImpl, ok := c.(*Container) - assert.True(ok) - - assert.True(status.StartTime.Equal(cImpl.process.StartTime)) - assert.Exactly(pImpl.config.Containers[0].Annotations, status.Annotations) -} - -func TestStatusContainerStateReady(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - // (homage to a great album! ;) - contID := "101" - - config := newTestSandboxConfigNoop() - cgroupPath, err := vccgroups.RenameCgroupPath(vccgroups.DefaultCgroupPath) - assert.NoError(err) - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - s, ok := p.(*Sandbox) - assert.True(ok) - sandboxDir := filepath.Join(s.newStore.RunStoragePath(), p.ID()) - _, err = os.Stat(sandboxDir) - assert.NoError(err) - - contConfig := newTestContainerConfigNoop(contID) - - _, c, err := CreateContainer(ctx, p.ID(), contConfig) - assert.NoError(err) - assert.NotNil(c) - - contDir := filepath.Join(sandboxDir, contID) - _, err = os.Stat(contDir) - assert.NoError(err) - - // fresh lookup - p2, err := fetchSandbox(ctx, p.ID()) - assert.NoError(err) - defer p2.releaseStatelessSandbox() - - expectedStatus := ContainerStatus{ - ID: contID, - State: types.ContainerState{ - State: types.StateReady, - CgroupPath: cgroupPath, - }, - PID: 0, - RootFs: filepath.Join(testDir, testBundle), - Annotations: containerAnnotations, - Spec: newEmptySpec(), - } - - defer p2.wg.Wait() - - status, err := statusContainer(p2, contID) - assert.NoError(err) - - // Copy the start time as we can't pretend we know what that - // value will be. - expectedStatus.StartTime = status.StartTime - - assert.Exactly(expectedStatus, status) -} - -func TestStatusContainerStateRunning(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - // (homage to a great album! ;) - contID := "101" - - config := newTestSandboxConfigNoop() - cgroupPath, err := vccgroups.RenameCgroupPath(vccgroups.DefaultCgroupPath) - assert.NoError(err) - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - p, err = StartSandbox(ctx, p.ID()) - assert.NoError(err) - assert.NotNil(p) - - s, ok := p.(*Sandbox) - assert.True(ok) - sandboxDir := filepath.Join(s.newStore.RunStoragePath(), p.ID()) - _, err = os.Stat(sandboxDir) - assert.NoError(err) - - contConfig := newTestContainerConfigNoop(contID) - - _, c, err := CreateContainer(ctx, p.ID(), contConfig) - assert.NoError(err) - assert.NotNil(c) - - c, err = StartContainer(ctx, p.ID(), c.ID()) - assert.NoError(err) - assert.NotNil(c) - - contDir := filepath.Join(sandboxDir, contID) - _, err = os.Stat(contDir) - assert.NoError(err) - - // fresh lookup - p2, err := fetchSandbox(ctx, p.ID()) - assert.NoError(err) - defer p2.releaseStatelessSandbox() - - expectedStatus := ContainerStatus{ - ID: contID, - State: types.ContainerState{ - State: types.StateRunning, - CgroupPath: cgroupPath, - }, - PID: 0, - RootFs: filepath.Join(testDir, testBundle), - Annotations: containerAnnotations, - Spec: newEmptySpec(), - } - - defer p2.wg.Wait() - - status, err := statusContainer(p2, contID) - assert.NoError(err) - - // Copy the start time as we can't pretend we know what that - // value will be. - expectedStatus.StartTime = status.StartTime - - assert.Exactly(expectedStatus, status) -} - -func TestStatusContainerFailing(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - contID := "100" - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - rmSandboxDir(p.ID()) - globalSandboxList.removeSandbox(p.ID()) - - _, err = StatusContainer(ctx, p.ID(), contID) - assert.Error(err) -} - -func TestStatsContainerFailing(t *testing.T) { - defer cleanUp() - assert := assert.New(t) - - contID := "100" - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - rmSandboxDir(p.ID()) - globalSandboxList.removeSandbox(p.ID()) - - _, err = StatsContainer(ctx, p.ID(), contID) - assert.Error(err) -} - -func TestStatsContainer(t *testing.T) { - defer cleanUp() - - assert := assert.New(t) - contID := "100" - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - _, err := StatsContainer(ctx, "", "") - assert.Error(err) - - _, err = StatsContainer(ctx, "abc", "") - assert.Error(err) - - _, err = StatsContainer(ctx, "abc", "abc") - assert.Error(err) - - config := newTestSandboxConfigNoop() - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - p, err = StartSandbox(ctx, p.ID()) - assert.NoError(err) - assert.NotNil(p) - - pImpl, ok := p.(*Sandbox) - assert.True(ok) - - contConfig := newTestContainerConfigNoop(contID) - _, c, err := CreateContainer(ctx, p.ID(), contConfig) - assert.NoError(err) - assert.NotNil(c) - - _, err = StatsContainer(ctx, pImpl.id, "xyz") - assert.Error(err) - - _, err = StatsContainer(ctx, "xyz", contID) - assert.Error(err) - - stats, err := StatsContainer(ctx, pImpl.id, contID) - assert.NoError(err) - assert.Equal(ContainerStats{}, stats) -} - -func TestProcessListContainer(t *testing.T) { - defer cleanUp() - - assert := assert.New(t) - - contID := "abc" - options := ProcessListOptions{ - Format: "json", - Args: []string{"-ef"}, - } - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - _, err := ProcessListContainer(ctx, "", "", options) - assert.Error(err) - - _, err = ProcessListContainer(ctx, "xyz", "", options) - assert.Error(err) - - _, err = ProcessListContainer(ctx, "xyz", "xyz", options) - assert.Error(err) - - config := newTestSandboxConfigNoop() - p, err := CreateSandbox(ctx, config, nil) - assert.NoError(err) - assert.NotNil(p) - - pImpl, ok := p.(*Sandbox) - assert.True(ok) - - contConfig := newTestContainerConfigNoop(contID) - _, c, err := CreateContainer(ctx, p.ID(), contConfig) - assert.NoError(err) - assert.NotNil(c) - - _, err = ProcessListContainer(ctx, pImpl.id, "xyz", options) - assert.Error(err) - - _, err = ProcessListContainer(ctx, "xyz", contID, options) - assert.Error(err) - - _, err = ProcessListContainer(ctx, pImpl.id, contID, options) - // Sandbox not running, impossible to ps the container - assert.Error(err) -} - /* * Benchmarks */ @@ -1448,6 +239,19 @@ func createNewSandboxConfig(hType HypervisorType) SandboxConfig { } } +func newTestContainerConfigNoop(contID string) ContainerConfig { + // Define the container command and bundle. + container := ContainerConfig{ + ID: contID, + RootFs: RootFs{Target: filepath.Join(testDir, testBundle), Mounted: true}, + Cmd: newBasicTestCmd(), + Annotations: containerAnnotations, + CustomSpec: newEmptySpec(), + } + + return container +} + // createAndStartSandbox handles the common test operation of creating and // starting a sandbox. func createAndStartSandbox(ctx context.Context, config SandboxConfig) (sandbox VCSandbox, sandboxDir string, @@ -1470,89 +274,14 @@ func createAndStartSandbox(ctx context.Context, config SandboxConfig) (sandbox V } // Start sandbox - sandbox, err = StartSandbox(ctx, sandbox.ID()) - if sandbox == nil || err != nil { + err = sandbox.Start() + if err != nil { return nil, "", err } return sandbox, sandboxDir, nil } -func createStartStopDeleteSandbox(b *testing.B, sandboxConfig SandboxConfig) { - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - - p, _, err := createAndStartSandbox(ctx, sandboxConfig) - if p == nil || err != nil { - b.Fatalf("Could not create and start sandbox: %s", err) - } - - // Stop sandbox - _, err = StopSandbox(ctx, p.ID(), false) - if err != nil { - b.Fatalf("Could not stop sandbox: %s", err) - } - - // Delete sandbox - _, err = DeleteSandbox(ctx, p.ID()) - if err != nil { - b.Fatalf("Could not delete sandbox: %s", err) - } -} - -func BenchmarkCreateStartStopDeleteSandboxQemuHypervisorNoopAgentNetworkNoop(b *testing.B) { - for i := 0; i < b.N; i++ { - sandboxConfig := createNewSandboxConfig(QemuHypervisor) - createStartStopDeleteSandbox(b, sandboxConfig) - } -} - -func BenchmarkCreateStartStopDeleteSandboxMockHypervisorNoopAgentNetworkNoop(b *testing.B) { - for i := 0; i < b.N; i++ { - sandboxConfig := createNewSandboxConfig(MockHypervisor) - createStartStopDeleteSandbox(b, sandboxConfig) - } -} - -func TestFetchSandbox(t *testing.T) { - defer cleanUp() - - config := newTestSandboxConfigNoop() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - s, err := CreateSandbox(ctx, config, nil) - assert.NoError(t, err) - assert.NotNil(t, s) - - fetched, err := FetchSandbox(ctx, s.ID()) - assert.Nil(t, err, "%v", err) - assert.True(t, fetched != s, "fetched stateless sandboxes should not match") -} - -func TestFetchStatefulSandbox(t *testing.T) { - defer cleanUp() - - config := newTestSandboxConfigNoop() - - config.Stateful = true - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - s, err := CreateSandbox(ctx, config, nil) - assert.NoError(t, err) - assert.NotNil(t, s) - - fetched, err := FetchSandbox(ctx, s.ID()) - assert.Nil(t, err, "%v", err) - assert.Equal(t, fetched, s, "fetched stateful sandboxed should match") -} - -func TestFetchNonExistingSandbox(t *testing.T) { - defer cleanUp() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - _, err := FetchSandbox(ctx, "some-non-existing-sandbox-name") - assert.NotNil(t, err, "fetch non-existing sandbox should fail") -} - func TestReleaseSandbox(t *testing.T) { defer cleanUp() @@ -1567,158 +296,6 @@ func TestReleaseSandbox(t *testing.T) { assert.Nil(t, err, "sandbox release failed: %v", err) } -func TestUpdateContainer(t *testing.T) { - if tc.NotValid(ktu.NeedRoot()) { - t.Skip(testDisabledAsNonRoot) - } - - defer cleanUp() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - - period := uint64(1000) - quota := int64(2000) - memoryLimit := int64(1073741824) - memorySwap := int64(1073741824) - assert := assert.New(t) - resources := specs.LinuxResources{ - CPU: &specs.LinuxCPU{ - Period: &period, - Quota: "a, - }, - Memory: &specs.LinuxMemory{ - Limit: &memoryLimit, - Swap: &memorySwap, - }, - } - err := UpdateContainer(ctx, "", "", resources) - assert.Error(err) - - err = UpdateContainer(ctx, "abc", "", resources) - assert.Error(err) - - contID := "100" - config := newTestSandboxConfigNoop() - - s, sandboxDir, err := createAndStartSandbox(ctx, config) - assert.NoError(err) - assert.NotNil(s) - - contConfig := newTestContainerConfigNoop(contID) - _, c, err := CreateContainer(ctx, s.ID(), contConfig) - assert.NoError(err) - assert.NotNil(c) - - contDir := filepath.Join(sandboxDir, contID) - _, err = os.Stat(contDir) - assert.NoError(err) - - _, err = StartContainer(ctx, s.ID(), contID) - assert.NoError(err) - - err = UpdateContainer(ctx, s.ID(), contID, resources) - assert.NoError(err) -} - -func TestPauseResumeContainer(t *testing.T) { - if tc.NotValid(ktu.NeedRoot()) { - t.Skip(testDisabledAsNonRoot) - } - - defer cleanUp() - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - - assert := assert.New(t) - err := PauseContainer(ctx, "", "") - assert.Error(err) - - err = PauseContainer(ctx, "abc", "") - assert.Error(err) - - contID := "100" - config := newTestSandboxConfigNoop() - - s, sandboxDir, err := createAndStartSandbox(ctx, config) - assert.NoError(err) - assert.NotNil(s) - - contConfig := newTestContainerConfigNoop(contID) - _, c, err := CreateContainer(ctx, s.ID(), contConfig) - assert.NoError(err) - assert.NotNil(c) - - contDir := filepath.Join(sandboxDir, contID) - _, err = os.Stat(contDir) - assert.NoError(err) - - _, err = StartContainer(ctx, s.ID(), contID) - assert.NoError(err) - - err = PauseContainer(ctx, s.ID(), contID) - assert.NoError(err) - - err = ResumeContainer(ctx, s.ID(), contID) - assert.NoError(err) -} - -func TestNetworkOperation(t *testing.T) { - if tc.NotValid(ktu.NeedRoot()) { - t.Skip(testDisabledAsNonRoot) - } - - defer cleanUp() - - assert := assert.New(t) - inf := &vcTypes.Interface{ - Name: "eno1", - Mtu: 1500, - HwAddr: "02:00:ca:fe:00:48", - } - ip := vcTypes.IPAddress{ - Family: 0, - Address: "192.168.0.101", - Mask: "24", - } - inf.IPAddresses = append(inf.IPAddresses, &ip) - - ctx := WithNewAgentFunc(context.Background(), newMockAgent) - - _, err := AddInterface(ctx, "", inf) - assert.Error(err) - - _, err = AddInterface(ctx, "abc", inf) - assert.Error(err) - - netNSPath, err := createNetNS() - assert.NoError(err) - defer deleteNetNS(netNSPath) - - config := newTestSandboxConfigNoop() - config.NetworkConfig = NetworkConfig{ - NetNSPath: netNSPath, - } - - s, _, err := createAndStartSandbox(ctx, config) - assert.NoError(err) - assert.NotNil(s) - - _, err = AddInterface(ctx, s.ID(), inf) - assert.Error(err) - - _, err = RemoveInterface(ctx, s.ID(), inf) - assert.NoError(err) - - _, err = ListInterfaces(ctx, s.ID()) - assert.NoError(err) - - _, err = UpdateRoutes(ctx, s.ID(), nil) - assert.NoError(err) - - _, err = ListRoutes(ctx, s.ID()) - assert.NoError(err) -} - func TestCleanupContainer(t *testing.T) { config := newTestSandboxConfigNoop() assert := assert.New(t) diff --git a/src/runtime/virtcontainers/documentation/api/1.0/api.md b/src/runtime/virtcontainers/documentation/api/1.0/api.md index 11457663b1..f63f3e6d88 100644 --- a/src/runtime/virtcontainers/documentation/api/1.0/api.md +++ b/src/runtime/virtcontainers/documentation/api/1.0/api.md @@ -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) + } } ``` diff --git a/src/runtime/virtcontainers/example_pod_run_test.go b/src/runtime/virtcontainers/example_pod_run_test.go index ae2bc88b64..bf7f11fa80 100644 --- a/src/runtime/virtcontainers/example_pod_run_test.go +++ b/src/runtime/virtcontainers/example_pod_run_test.go @@ -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) } } diff --git a/src/runtime/virtcontainers/implementation.go b/src/runtime/virtcontainers/implementation.go index b126da00d5..dc2ee0aaa5 100644 --- a/src/runtime/virtcontainers/implementation.go +++ b/src/runtime/virtcontainers/implementation.go @@ -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. diff --git a/src/runtime/virtcontainers/interfaces.go b/src/runtime/virtcontainers/interfaces.go index 95ac941e6c..7ba539772e 100644 --- a/src/runtime/virtcontainers/interfaces.go +++ b/src/runtime/virtcontainers/interfaces.go @@ -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 } diff --git a/src/runtime/virtcontainers/pkg/vcmock/sandbox.go b/src/runtime/virtcontainers/pkg/vcmock/sandbox.go index 2019989e2e..938c57ffa8 100644 --- a/src/runtime/virtcontainers/pkg/vcmock/sandbox.go +++ b/src/runtime/virtcontainers/pkg/vcmock/sandbox.go @@ -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. diff --git a/src/runtime/virtcontainers/pkg/vcmock/utils.go b/src/runtime/virtcontainers/pkg/vcmock/utils.go index d2e5433f37..6809a56cfb 100644 --- a/src/runtime/virtcontainers/pkg/vcmock/utils.go +++ b/src/runtime/virtcontainers/pkg/vcmock/utils.go @@ -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) }