From b568054acee813a110e46f2def5e8e0080691565 Mon Sep 17 00:00:00 2001 From: "James O. D. Hunt" Date: Wed, 23 Aug 2017 17:37:38 +0100 Subject: [PATCH] tests: Increase unit-test coverage on create.go Added 18 new tests. Coverage (when run as root) is 87.1% (actually, it's higher since some tests require non-root to run). Fixes #401. Signed-off-by: James O. D. Hunt --- create.go | 13 +- create_test.go | 921 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 929 insertions(+), 5 deletions(-) diff --git a/create.go b/create.go index e57f406f..289917e5 100644 --- a/create.go +++ b/create.go @@ -84,6 +84,8 @@ var createCLICommand = cli.Command{ }, } +var getKernelParamsFunc func(containerID string) []vc.Param = getKernelParams + func create(containerID, bundlePath, console, pidFilePath string, detach bool, runtimeConfig oci.RuntimeConfig) error { var err error @@ -144,10 +146,8 @@ func create(containerID, bundlePath, console, pidFilePath string, detach bool, return nil } -func createPod(ociSpec oci.CompatOCISpec, runtimeConfig oci.RuntimeConfig, - containerID, bundlePath, console string, disableOutput bool) (vc.Process, error) { - - ccKernelParams := []vc.Param{ +func getKernelParams(containerID string) []vc.Param { + return []vc.Param{ { Key: "init", Value: "/usr/lib/systemd/systemd", @@ -169,6 +169,11 @@ func createPod(ociSpec oci.CompatOCISpec, runtimeConfig oci.RuntimeConfig, Value: fmt.Sprintf("::::::%s::off::", containerID), }, } +} + +func createPod(ociSpec oci.CompatOCISpec, runtimeConfig oci.RuntimeConfig, + containerID, bundlePath, console string, disableOutput bool) (vc.Process, error) { + ccKernelParams := getKernelParamsFunc(containerID) for _, p := range ccKernelParams { if err := (&runtimeConfig).AddKernelParam(p); err != nil { diff --git a/create_test.go b/create_test.go index 013089b1..a39b9bc0 100644 --- a/create_test.go +++ b/create_test.go @@ -15,14 +15,31 @@ package main import ( + "flag" "fmt" "io/ioutil" "os" "path/filepath" + "strings" "testing" + + vc "github.com/containers/virtcontainers" + "github.com/containers/virtcontainers/pkg/oci" + "github.com/containers/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" + testSandboxIDAnnotation = "io.kubernetes.cri-o.SandboxID" + testContainerTypePod = "sandbox" + testContainerTypeContainer = "container" ) -var testPID = 100 var testStrPID = fmt.Sprintf("%d", testPID) func testCreateCgroupsFilesSuccessful(t *testing.T, cgroupsPathList []string, pid int) { @@ -95,3 +112,905 @@ func TestCreatePIDFileEmptyPathSuccessful(t *testing.T) { t.Fatalf("This test should not fail (pidFilePath %q, pid %d)", file, testPID) } } + +func TestCreatePIDFileUnableToRemove(t *testing.T) { + if os.Geteuid() == 0 { + // The os.FileMode(0000) trick doesn't work for root. + t.Skip(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(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(file, testPID) + + // subdir doesn't exist + assert.Error(err) + os.Chmod(subdir, testDirMode) +} + +func TestCreateCLIFunctionNoRuntimeConfig(t *testing.T) { + assert := assert.New(t) + + app := cli.NewApp() + ctx := cli.NewContext(app, nil, nil) + app.Name = "foo" + ctx.App.Metadata = map[string]interface{}{ + "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, "") + + app := cli.NewApp() + ctx := cli.NewContext(app, set, nil) + app.Name = "foo" + + ctx.App.Metadata = map[string]interface{}{ + "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", "", "") + + app := cli.NewApp() + ctx := cli.NewContext(app, set, nil) + app.Name = "foo" + + ctx.App.Metadata = map[string]interface{}{ + "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) + + pod := &vcMock.Pod{ + MockID: testPodID, + MockContainers: []*vcMock.Container{ + {MockID: testContainerID}, + {MockID: testContainerID}, + {MockID: testContainerID}, + }, + } + + testingImpl.CreatePodFunc = func(podConfig vc.PodConfig) (vc.VCPod, error) { + return pod, nil + } + + testingImpl.ListPodFunc = func() ([]vc.PodStatus, error) { + // No pre-existing pods + return []vc.PodStatus{}, nil + } + + defer func() { + testingImpl.CreatePodFunc = nil + testingImpl.ListPodFunc = 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 = os.MkdirAll(bundlePath, testDirMode) + assert.NoError(err) + + 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 + runtimeConfig oci.RuntimeConfig + expectFail bool + } + + // FIXME: need a passing test here! + data := []testData{ + {"", "", "", "", false, oci.RuntimeConfig{}, true}, + {"", "", "", "", true, oci.RuntimeConfig{}, true}, + {"foo", "", "", "", true, oci.RuntimeConfig{}, true}, + {testContainerID, bundlePath, testConsole, pidFilePath, false, runtimeConfig, true}, + {testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig, true}, + } + + for i, d := range data { + err := create(d.containerID, d.bundlePath, d.console, d.pidFilePath, d.detach, d.runtimeConfig) + if d.expectFail { + // FIXME: + fmt.Printf("DEBUG: TestCreateInvalidArgs: i: %v, d: %+v, err: %v\n", i, d, err) + assert.Error(err, "test %d (%+v)", i, d) + // FIXME: Need to add "vcMockError bool" to struct! + //assert.False(vcMock.IsMockError(err)) + } else { + assert.NoError(err, "%d: (%+v)", i, d) + } + } +} + +func TestCreateInvalidConfigJSON(t *testing.T) { + assert := assert.New(t) + + testingImpl.ListPodFunc = func() ([]vc.PodStatus, error) { + // No pre-existing pods + return []vc.PodStatus{}, nil + } + + defer func() { + testingImpl.ListPodFunc = 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 = os.MkdirAll(bundlePath, testDirMode) + assert.NoError(err) + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + pidFilePath := filepath.Join(tmpdir, "pidfile.txt") + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(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(testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig) + assert.Error(err, "%+v", detach) + assert.False(vcMock.IsMockError(err)) + } +} + +func TestCreateInvalidContainerType(t *testing.T) { + assert := assert.New(t) + + testingImpl.ListPodFunc = func() ([]vc.PodStatus, error) { + // No pre-existing pods + return []vc.PodStatus{}, nil + } + + defer func() { + testingImpl.ListPodFunc = 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 = os.MkdirAll(bundlePath, testDirMode) + assert.NoError(err) + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + pidFilePath := filepath.Join(tmpdir, "pidfile.txt") + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(fileExists(ociConfigFile)) + + spec, err := readOCIConfigFile(ociConfigFile) + 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(testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig) + assert.Error(err, "%+v", detach) + assert.False(vcMock.IsMockError(err)) + } +} + +func TestCreateContainerInvalid(t *testing.T) { + assert := assert.New(t) + + testingImpl.ListPodFunc = func() ([]vc.PodStatus, error) { + // No pre-existing pods + return []vc.PodStatus{}, nil + } + + defer func() { + testingImpl.ListPodFunc = 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 = os.MkdirAll(bundlePath, testDirMode) + assert.NoError(err) + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + pidFilePath := filepath.Join(tmpdir, "pidfile.txt") + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(fileExists(ociConfigFile)) + + spec, err := readOCIConfigFile(ociConfigFile) + + 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(testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig) + assert.Error(err, "%+v", detach) + assert.False(vcMock.IsMockError(err)) + } +} + +func TestCreateProcessCgroupsPathFail(t *testing.T) { + assert := assert.New(t) + + pod := &vcMock.Pod{ + MockID: testPodID, + MockContainers: []*vcMock.Container{ + {MockID: testContainerID}, + }, + } + + testingImpl.ListPodFunc = func() ([]vc.PodStatus, error) { + // No pre-existing pods + return []vc.PodStatus{}, nil + } + + testingImpl.CreatePodFunc = func(podConfig vc.PodConfig) (vc.VCPod, error) { + return pod, nil + } + + defer func() { + testingImpl.ListPodFunc = nil + testingImpl.CreatePodFunc = 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 = os.MkdirAll(bundlePath, testDirMode) + assert.NoError(err) + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + pidFilePath := filepath.Join(tmpdir, "pidfile.txt") + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(fileExists(ociConfigFile)) + + spec, err := readOCIConfigFile(ociConfigFile) + assert.NoError(err) + + // Force pod-type container + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = testContainerTypePod + + // Set a limit to ensure processCgroupsPath() consider the + // cgroup part of the spec + limit := uint64(1024 * 1024) + spec.Linux.Resources.Memory = &specs.LinuxMemory{ + Limit: &limit, + } + + // Set an absolute (and invalid) path + spec.Linux.CgroupsPath = "/this/is/not/a/valid/cgroup/path" + + var mounts []specs.Mount + foundMount := false + + for _, mount := range spec.Mounts { + if mount.Type == "cgroup" { + foundMount = true + } else { + mounts = append(mounts, mount) + } + } + + assert.True(foundMount) + + // Remove the cgroup mount + spec.Mounts = mounts + + // Rewrite the file + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + for detach := range []bool{true, false} { + err := create(testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig) + assert.Error(err, "%+v", detach) + assert.False(vcMock.IsMockError(err)) + } +} + +func TestCreateCreateCgroupsFilesFail(t *testing.T) { + if os.Geteuid() == 0 { + // The os.FileMode(0000) trick doesn't work for root. + t.Skip(testDisabledNeedNonRoot) + } + + assert := assert.New(t) + + pod := &vcMock.Pod{ + MockID: testPodID, + MockContainers: []*vcMock.Container{ + {MockID: testContainerID}, + }, + } + + testingImpl.ListPodFunc = func() ([]vc.PodStatus, error) { + // No pre-existing pods + return []vc.PodStatus{}, nil + } + + testingImpl.CreatePodFunc = func(podConfig vc.PodConfig) (vc.VCPod, error) { + return pod, nil + } + + defer func() { + testingImpl.ListPodFunc = nil + testingImpl.CreatePodFunc = 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 = os.MkdirAll(bundlePath, testDirMode) + assert.NoError(err) + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + pidFilePath := filepath.Join(tmpdir, "pidfile.txt") + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(fileExists(ociConfigFile)) + + spec, err := readOCIConfigFile(ociConfigFile) + assert.NoError(err) + + // Force pod-type container + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = testContainerTypePod + + // Set a limit to ensure processCgroupsPath() consider the + // cgroup part of the spec + limit := uint64(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") + + 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(testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig) + assert.Error(err, "%+v", detach) + assert.False(vcMock.IsMockError(err)) + } +} + +func TestCreateCreateCreatePidFileFail(t *testing.T) { + if os.Geteuid() == 0 { + // The os.FileMode(0000) trick doesn't work for root. + t.Skip(testDisabledNeedNonRoot) + } + + assert := assert.New(t) + + pod := &vcMock.Pod{ + MockID: testPodID, + MockContainers: []*vcMock.Container{ + {MockID: testContainerID}, + }, + } + + testingImpl.ListPodFunc = func() ([]vc.PodStatus, error) { + // No pre-existing pods + return []vc.PodStatus{}, nil + } + + testingImpl.CreatePodFunc = func(podConfig vc.PodConfig) (vc.VCPod, error) { + return pod, nil + } + + defer func() { + testingImpl.ListPodFunc = nil + testingImpl.CreatePodFunc = 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 = os.MkdirAll(bundlePath, testDirMode) + assert.NoError(err) + + 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(fileExists(ociConfigFile)) + + spec, err := readOCIConfigFile(ociConfigFile) + assert.NoError(err) + + // Force pod-type container + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = testContainerTypePod + + // Set a limit to ensure processCgroupsPath() consider the + // cgroup part of the spec + limit := uint64(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(testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig) + assert.Error(err, "%+v", detach) + assert.False(vcMock.IsMockError(err)) + } +} + +func TestCreateInvalidKernelParams(t *testing.T) { + assert := assert.New(t) + + testingImpl.ListPodFunc = func() ([]vc.PodStatus, error) { + // No pre-existing pods + return []vc.PodStatus{}, nil + } + + defer func() { + testingImpl.ListPodFunc = 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 = os.MkdirAll(bundlePath, testDirMode) + assert.NoError(err) + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + pidFilePath := filepath.Join(tmpdir, "pidfile.txt") + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(fileExists(ociConfigFile)) + + spec, err := readOCIConfigFile(ociConfigFile) + assert.NoError(err) + + // Force createPod() to be called. + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = testContainerTypePod + + // rewrite the file + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + savedFunc := getKernelParamsFunc + defer func() { + getKernelParamsFunc = savedFunc + }() + + getKernelParamsFunc = func(containerID string) []vc.Param { + return []vc.Param{ + { + Key: "", + Value: "", + }, + } + } + + for detach := range []bool{true, false} { + err := create(testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig) + assert.Error(err, "%+v", detach) + assert.False(vcMock.IsMockError(err)) + } +} + +func TestCreateCreatePodPodConfigFail(t *testing.T) { + assert := assert.New(t) + + testingImpl.ListPodFunc = func() ([]vc.PodStatus, error) { + // No pre-existing pods + return []vc.PodStatus{}, nil + } + + defer func() { + testingImpl.ListPodFunc = 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 = os.MkdirAll(bundlePath, testDirMode) + assert.NoError(err) + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(fileExists(ociConfigFile)) + + spec, err := readOCIConfigFile(ociConfigFile) + assert.NoError(err) + + quota := int64(0) + limit := uint64(0) + + spec.Linux.Resources.Memory = &specs.LinuxMemory{ + Limit: &limit, + } + + spec.Linux.Resources.CPU = &specs.LinuxCPU{ + // specify an invalid value + Quota: "a, + } + + _, err = createPod(spec, runtimeConfig, testContainerID, bundlePath, testConsole, true) + assert.Error(err) + assert.False(vcMock.IsMockError(err)) +} + +func TestCreateCreatePodFail(t *testing.T) { + assert := assert.New(t) + + testingImpl.ListPodFunc = func() ([]vc.PodStatus, error) { + // No pre-existing pods + return []vc.PodStatus{}, nil + } + + defer func() { + testingImpl.ListPodFunc = 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 = os.MkdirAll(bundlePath, testDirMode) + assert.NoError(err) + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(fileExists(ociConfigFile)) + + spec, err := readOCIConfigFile(ociConfigFile) + assert.NoError(err) + + _, err = createPod(spec, runtimeConfig, testContainerID, bundlePath, testConsole, true) + assert.Error(err) + assert.True(vcMock.IsMockError(err)) +} + +func TestCreateCreateContainerContainerConfigFail(t *testing.T) { + assert := assert.New(t) + + testingImpl.ListPodFunc = func() ([]vc.PodStatus, error) { + // No pre-existing pods + return []vc.PodStatus{}, nil + } + + defer func() { + testingImpl.ListPodFunc = nil + }() + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = os.MkdirAll(bundlePath, testDirMode) + assert.NoError(err) + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(fileExists(ociConfigFile)) + + spec, err := readOCIConfigFile(ociConfigFile) + assert.NoError(err) + + // Set invalid container type + containerType := "你好,世界" + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = containerType + + // rewrite file + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + for _, disableOutput := range []bool{true, false} { + _, err = createContainer(spec, testContainerID, bundlePath, testConsole, disableOutput) + assert.Error(err) + assert.False(vcMock.IsMockError(err)) + assert.True(strings.Contains(err.Error(), containerType)) + } +} + +func TestCreateCreateContainerFail(t *testing.T) { + assert := assert.New(t) + + testingImpl.ListPodFunc = func() ([]vc.PodStatus, error) { + // No pre-existing pods + return []vc.PodStatus{}, nil + } + + defer func() { + testingImpl.ListPodFunc = nil + }() + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = os.MkdirAll(bundlePath, testDirMode) + assert.NoError(err) + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(fileExists(ociConfigFile)) + + spec, err := readOCIConfigFile(ociConfigFile) + assert.NoError(err) + + // set expected container type and podID + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = testContainerTypeContainer + spec.Annotations[testSandboxIDAnnotation] = testPodID + + // rewrite file + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + for _, disableOutput := range []bool{true, false} { + _, err = createContainer(spec, testContainerID, bundlePath, testConsole, disableOutput) + assert.Error(err) + assert.True(vcMock.IsMockError(err)) + } +} + +func TestCreateCreateContainer(t *testing.T) { + assert := assert.New(t) + + testingImpl.ListPodFunc = func() ([]vc.PodStatus, error) { + // No pre-existing pods + return []vc.PodStatus{}, nil + } + + testingImpl.CreateContainerFunc = func(podID string, containerConfig vc.ContainerConfig) (vc.VCPod, vc.VCContainer, error) { + return &vcMock.Pod{}, &vcMock.Container{}, nil + } + + defer func() { + testingImpl.ListPodFunc = nil + testingImpl.CreatePodFunc = nil + }() + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = os.MkdirAll(bundlePath, testDirMode) + assert.NoError(err) + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(fileExists(ociConfigFile)) + + spec, err := readOCIConfigFile(ociConfigFile) + assert.NoError(err) + + // set expected container type and podID + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = testContainerTypeContainer + spec.Annotations[testSandboxIDAnnotation] = testPodID + + // rewrite file + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + for _, disableOutput := range []bool{true, false} { + _, err = createContainer(spec, testContainerID, bundlePath, testConsole, disableOutput) + assert.NoError(err) + } +}