From 19b753f45318104ab83f5b2298bbe0ed465331d2 Mon Sep 17 00:00:00 2001 From: "James O. D. Hunt" Date: Fri, 28 Jul 2017 16:23:48 +0100 Subject: [PATCH] tests: Add framework to mock virtcontainers Add a simple test framework to allow the behaviour of the virtcontainers package to be mocked. This change introduces a RVC interface and provides two implementations: one for virtcontainers itself (rvc-implementation.go) and another mock implementation for the tests to allow the behaviour of the virtcontainers package to be modified (rvc-implementation_test.go). By only accessing the virtcontainers implementation via the "vci" variable, the tests can be switched to the mock implementation. Note that this mocking is used rather than testing virtcontainers directly since the virtcontainers API is already tested in that package: the mock framework thus avoids duplication of test code and allows more fine-grained error scenarios to be handled. This change also introduces a single new test, TestCreate(), that increases the unit test coverage of create.go by using the new framework to force create() to fail due to a virtcontainers failure in CreatePod(). Fixes #401. Signed-off-by: James O. D. Hunt --- create.go | 4 +- create_test.go | 41 ++++++ delete.go | 8 +- exec.go | 2 +- kill.go | 2 +- list.go | 3 +- main.go | 12 +- main_test.go | 214 ++++++++++++++++++++++++++++ oci.go | 2 +- pause.go | 5 +- pkg/rvc/rvc.go | 53 +++++++ rvc-implementation.go | 98 +++++++++++++ rvc-implementation_test.go | 278 +++++++++++++++++++++++++++++++++++++ start.go | 4 +- 14 files changed, 708 insertions(+), 18 deletions(-) create mode 100644 pkg/rvc/rvc.go create mode 100644 rvc-implementation.go create mode 100644 rvc-implementation_test.go diff --git a/create.go b/create.go index 479422d4..5b372016 100644 --- a/create.go +++ b/create.go @@ -183,7 +183,7 @@ func createPod(ociSpec oci.CompatOCISpec, runtimeConfig oci.RuntimeConfig, return vc.Process{}, err } - pod, err := vc.CreatePod(podConfig) + pod, err := vci.CreatePod(podConfig) if err != nil { return vc.Process{}, err } @@ -209,7 +209,7 @@ func createContainer(ociSpec oci.CompatOCISpec, containerID, bundlePath, return vc.Process{}, err } - _, c, err := vc.CreateContainer(podID, contConfig) + _, c, err := vci.CreateContainer(podID, contConfig) if err != nil { return vc.Process{}, err } diff --git a/create_test.go b/create_test.go index 013089b1..a360291c 100644 --- a/create_test.go +++ b/create_test.go @@ -20,6 +20,8 @@ import ( "os" "path/filepath" "testing" + + "github.com/stretchr/testify/assert" ) var testPID = 100 @@ -95,3 +97,42 @@ func TestCreatePIDFileEmptyPathSuccessful(t *testing.T) { t.Fatalf("This test should not fail (pidFilePath %q, pid %d)", file, testPID) } } + +func TestCreate(t *testing.T) { + // change behaviour + testingImpl.listPodFunc = listPodNoPods + testingImpl.forceFailure = true + + defer func() { + // reset + testingImpl.listPodFunc = nil + testingImpl.forceFailure = false + }() + + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + + defer os.RemoveAll(tmpdir) + + containerID := "foo" + bundlePath := filepath.Join(tmpdir, "bundle") + + err = os.MkdirAll(bundlePath, testDirMode) + assert.NoError(t, err) + + err = makeOCIBundle(bundlePath) + assert.NoError(t, err) + + console := "/dev/pts/999" + pidFilePath := filepath.Join(tmpdir, "pidfile.txt") + detach := false + runtimeConfig, err := newRuntimeConfig(tmpdir, console) + assert.NoError(t, err) + + err = create(containerID, bundlePath, console, pidFilePath, detach, runtimeConfig) + + assert.Error(t, err) + assert.True(t, isMockError(err)) +} diff --git a/delete.go b/delete.go index 28d188ba..d4115adc 100644 --- a/delete.go +++ b/delete.go @@ -114,11 +114,11 @@ func delete(containerID string, force bool) error { } func deletePod(podID string) error { - if _, err := vc.StopPod(podID); err != nil { + if _, err := vci.StopPod(podID); err != nil { return err } - if _, err := vc.DeletePod(podID); err != nil { + if _, err := vci.DeletePod(podID); err != nil { return err } @@ -127,12 +127,12 @@ func deletePod(podID string) error { func deleteContainer(podID, containerID string, forceStop bool) error { if forceStop { - if _, err := vc.StopContainer(podID, containerID); err != nil { + if _, err := vci.StopContainer(podID, containerID); err != nil { return err } } - if _, err := vc.DeleteContainer(podID, containerID); err != nil { + if _, err := vci.DeleteContainer(podID, containerID); err != nil { return err } diff --git a/exec.go b/exec.go index 151608b5..0c35036c 100644 --- a/exec.go +++ b/exec.go @@ -230,7 +230,7 @@ func execute(context *cli.Context) error { Detach: noNeedForOutput(params.detach, params.ociProcess.Terminal), } - _, _, process, err := vc.EnterContainer(podID, params.cID, cmd) + _, _, process, err := vci.EnterContainer(podID, params.cID, cmd) if err != nil { return err } diff --git a/kill.go b/kill.go index 3d0fc3ac..18e9591a 100644 --- a/kill.go +++ b/kill.go @@ -116,7 +116,7 @@ func kill(containerID, signal string, all bool) error { return fmt.Errorf("Container %s not ready or running, cannot send a signal", containerID) } - if err := vc.KillContainer(podID, containerID, signum, all); err != nil { + if err := vci.KillContainer(podID, containerID, signum, all); err != nil { return err } diff --git a/list.go b/list.go index 7e2c748d..7a99edfd 100644 --- a/list.go +++ b/list.go @@ -26,7 +26,6 @@ import ( "github.com/urfave/cli" - vc "github.com/containers/virtcontainers" oci "github.com/containers/virtcontainers/pkg/oci" ) @@ -200,7 +199,7 @@ func getContainers(context *cli.Context) ([]fullContainerState, error) { return nil, err } - podList, err := vc.ListPod() + podList, err := vci.ListPod() if err != nil { return nil, err } diff --git a/main.go b/main.go index 78b759c6..2a27880e 100644 --- a/main.go +++ b/main.go @@ -22,7 +22,7 @@ import ( "strings" "github.com/Sirupsen/logrus" - vc "github.com/containers/virtcontainers" + "github.com/clearcontainers/runtime/pkg/rvc" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/urfave/cli" ) @@ -50,6 +50,14 @@ NOTES: var ccLog = logrus.New() +// concrete virtcontainer implementation +var virtcontainersImpl = &vcImpl{} + +// vci is used to access a particular virtcontainers implementation. +// Normally, it refers to the official package, but is re-assigned in +// the tests to allow virtcontainers to be mocked. +var vci rvc.RVC = virtcontainersImpl + func beforeSubcommands(context *cli.Context) error { if userWantsUsage(context) || (context.NArg() == 1 && (context.Args()[0] == "cc-check")) { // No setup required if the user just @@ -80,7 +88,7 @@ func beforeSubcommands(context *cli.Context) error { } // Set virtcontainers logger. - vc.SetLogger(ccLog) + vci.SetLogger(ccLog) ignoreLogging := false if context.NArg() == 1 && context.Args()[0] == "cc-env" { diff --git a/main_test.go b/main_test.go index 5c917a99..61943345 100644 --- a/main_test.go +++ b/main_test.go @@ -20,10 +20,16 @@ import ( "fmt" "io/ioutil" "os" + "os/exec" "path" + "path/filepath" + "strings" "testing" "github.com/dlespiau/covertool/pkg/cover" + + vc "github.com/containers/virtcontainers" + "github.com/containers/virtcontainers/pkg/oci" ) const ( @@ -32,11 +38,31 @@ const ( testDirMode = os.FileMode(0750) testFileMode = os.FileMode(0640) testExeFileMode = os.FileMode(0750) + + testPodID = "99999999-9999-9999-99999999999999999" + testContainerID = "1" + testBundle = "bundle" + testKernel = "kernel" + testImage = "image" + testHypervisor = "hypervisor" + + MockHypervisor vc.HypervisorType = "mock" + NoopAgentType vc.AgentType = "noop" ) // package variables set in TestMain var testDir = "" +var testPodAnnotations = map[string]string{ + "pod.foo": "pod.bar", + "pod.hello": "pod.world", +} + +var testContainerAnnotations = map[string]string{ + "container.foo": "container.bar", + "container.hello": "container.world", +} + func runUnitTests(m *testing.M) { var err error @@ -83,3 +109,191 @@ func TestMain(m *testing.M) { func createEmptyFile(path string) (err error) { return ioutil.WriteFile(path, []byte(""), testFileMode) } + +func newTestCmd() vc.Cmd { + envs := []vc.EnvVar{ + { + Var: "PATH", + Value: "/bin:/usr/bin:/sbin:/usr/sbin", + }, + } + + cmd := vc.Cmd{ + Args: strings.Split("/bin/sh", " "), + Envs: envs, + WorkDir: "/", + } + + return cmd +} + +func newTestPodConfigNoop() vc.PodConfig { + // Define the container command and bundle. + container := vc.ContainerConfig{ + ID: testContainerID, + RootFs: filepath.Join(testDir, testBundle), + Cmd: newTestCmd(), + Annotations: testContainerAnnotations, + } + + // Sets the hypervisor configuration. + hypervisorConfig := vc.HypervisorConfig{ + KernelPath: filepath.Join(testDir, testKernel), + ImagePath: filepath.Join(testDir, testImage), + HypervisorPath: filepath.Join(testDir, testHypervisor), + } + + podConfig := vc.PodConfig{ + ID: testPodID, + HypervisorType: MockHypervisor, + HypervisorConfig: hypervisorConfig, + + AgentType: NoopAgentType, + + Containers: []vc.ContainerConfig{container}, + + Annotations: testPodAnnotations, + } + + return podConfig +} + +func newTestHypervisorConfig(dir string) (vc.HypervisorConfig, error) { + if dir == "" { + return vc.HypervisorConfig{}, fmt.Errorf("BUG: need directory") + } + + kernelPath := path.Join(dir, "kernel") + imagePath := path.Join(dir, "image") + hypervisorPath := path.Join(dir, "hypervisor") + + for _, file := range []string{kernelPath, imagePath, hypervisorPath} { + err := createEmptyFile(file) + if err != nil { + return vc.HypervisorConfig{}, err + } + } + + return vc.HypervisorConfig{ + KernelPath: kernelPath, + ImagePath: imagePath, + HypervisorPath: hypervisorPath, + HypervisorMachineType: "pc-lite", + }, nil +} + +func newRuntimeConfig(dir, consolePath string) (oci.RuntimeConfig, error) { + hypervisorConfig, err := newTestHypervisorConfig(dir) + if err != nil { + return oci.RuntimeConfig{}, err + } + + return oci.RuntimeConfig{ + HypervisorType: vc.QemuHypervisor, + HypervisorConfig: hypervisorConfig, + AgentType: vc.HyperstartAgent, + ProxyType: vc.CCProxyType, + ShimType: vc.CCShimType, + Console: consolePath, + }, nil +} + +// createOCIConfig creates an OCI configuration file ("config.json") in +// the bundle directory specified (which must exist). +func createOCIConfig(bundleDir string) error { + + if bundleDir == "" { + return fmt.Errorf("Need bundle directory") + } + + if !fileExists(bundleDir) { + return fmt.Errorf("Bundle directory %s does not exist", bundleDir) + } + + var configCmd string + + for _, cmd := range []string{"docker-runc", "runc"} { + fullPath, err := exec.LookPath(cmd) + if err == nil { + configCmd = fullPath + break + } + } + + if configCmd == "" { + return fmt.Errorf("Cannot find command to generate OCI config file") + } + + cwd, err := os.Getwd() + if err != nil { + return err + } + + err = os.Chdir(bundleDir) + if err != nil { + return err + } + + defer func() { + err = os.Chdir(cwd) + }() + + _, err := runCommand([]string{configCmd, "spec"}) + if err != nil { + return err + } + + specFile := filepath.Join(bundleDir, "config.json") + if !fileExists(specFile) { + return fmt.Errorf("generated OCI config file does not exist: %v", specFile) + } + + return nil +} + +// makeOCIBundle will create an OCI bundle (including the "config.json" +// config file) in the directory specified (which must already exist). +func makeOCIBundle(bundleDir string) error { + if bundleDir == "" { + return fmt.Errorf("Need bundle directory") + } + + if defaultPauseRootPath == "" { + return fmt.Errorf("BUG: defaultPauseRootPath unset") + } + + // make use of the existing pause bundle + _, err := runCommand([]string{"cp", "-a", defaultPauseRootPath, bundleDir}) + if err != nil { + return err + } + + err = createOCIConfig(bundleDir) + if err != nil { + return err + } + + // Note the unusual parameter! + spec, err := oci.ParseConfigJSON(bundleDir) + if err != nil { + return err + } + + // Determine the rootfs directory name the OCI config refers to + rootDir := spec.Root.Path + + base := filepath.Base(defaultPauseRootPath) + from := filepath.Join(bundleDir, base) + to := rootDir + + if !strings.HasPrefix(rootDir, "/") { + to = filepath.Join(bundleDir, rootDir) + } + + _, err = runCommand([]string{"mv", from, to}) + if err != nil { + return err + } + + return nil +} diff --git a/oci.go b/oci.go index 2333aa52..3206bb12 100644 --- a/oci.go +++ b/oci.go @@ -62,7 +62,7 @@ func getContainerInfo(containerID string) (vc.ContainerStatus, string, error) { return vc.ContainerStatus{}, "", fmt.Errorf("Missing container ID") } - podStatusList, err := vc.ListPod() + podStatusList, err := vci.ListPod() if err != nil { return vc.ContainerStatus{}, "", err } diff --git a/pause.go b/pause.go index bd07bdc1..807943f7 100644 --- a/pause.go +++ b/pause.go @@ -16,7 +16,6 @@ package main import ( - vc "github.com/containers/virtcontainers" "github.com/urfave/cli" ) @@ -58,9 +57,9 @@ func toggleContainerPause(containerID string, pause bool) (err error) { } if pause { - _, err = vc.PausePod(podID) + _, err = vci.PausePod(podID) } else { - _, err = vc.ResumePod(podID) + _, err = vci.ResumePod(podID) } return err diff --git a/pkg/rvc/rvc.go b/pkg/rvc/rvc.go new file mode 100644 index 00000000..e747d952 --- /dev/null +++ b/pkg/rvc/rvc.go @@ -0,0 +1,53 @@ +// Copyright (c) 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Description: This file introduces an interface any +// virtcontainers-compatible implementations must conform to. +// It is used to allow the underlying virtcontainers package to be +// switched between the official package and any number +// of other implementations for testing purposes. + +package rvc + +import ( + "syscall" + + "github.com/Sirupsen/logrus" + + // All implementations need to manipulate the official types + vc "github.com/containers/virtcontainers" +) + +// RVC is a Runtime Virtcontainers implementation +type RVC interface { + SetLogger(logger logrus.FieldLogger) + + CreatePod(podConfig vc.PodConfig) (*vc.Pod, error) + DeletePod(podID string) (*vc.Pod, error) + StartPod(podID string) (*vc.Pod, error) + StopPod(podID string) (*vc.Pod, error) + RunPod(podConfig vc.PodConfig) (*vc.Pod, error) + ListPod() ([]vc.PodStatus, error) + StatusPod(podID string) (vc.PodStatus, error) + PausePod(podID string) (*vc.Pod, error) + ResumePod(podID string) (*vc.Pod, error) + + CreateContainer(podID string, containerConfig vc.ContainerConfig) (*vc.Pod, *vc.Container, error) + DeleteContainer(podID, containerID string) (*vc.Container, error) + StartContainer(podID, containerID string) (*vc.Container, error) + StopContainer(podID, containerID string) (*vc.Container, error) + EnterContainer(podID, containerID string, cmd vc.Cmd) (*vc.Pod, *vc.Container, *vc.Process, error) + StatusContainer(podID, containerID string) (vc.ContainerStatus, error) + KillContainer(podID, containerID string, signal syscall.Signal, all bool) error +} diff --git a/rvc-implementation.go b/rvc-implementation.go new file mode 100644 index 00000000..12a36fa1 --- /dev/null +++ b/rvc-implementation.go @@ -0,0 +1,98 @@ +// Copyright (c) 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Description: The true virtcontainers implementation of the RVC interface. +// This indirection is required to allow an alternative implemenation to be +// used for testing purposes. + +package main + +import ( + "syscall" + + "github.com/Sirupsen/logrus" + vc "github.com/containers/virtcontainers" +) + +// virtcontainers implementation type +type vcImpl struct { +} + +func (impl *vcImpl) SetLogger(logger logrus.FieldLogger) { + vc.SetLogger(logger) +} + +func (impl *vcImpl) CreatePod(podConfig vc.PodConfig) (*vc.Pod, error) { + return vc.CreatePod(podConfig) +} + +func (impl *vcImpl) DeletePod(podID string) (*vc.Pod, error) { + return vc.DeletePod(podID) +} + +func (impl *vcImpl) StartPod(podID string) (*vc.Pod, error) { + return vc.StartPod(podID) +} + +func (impl *vcImpl) StopPod(podID string) (*vc.Pod, error) { + return vc.StopPod(podID) +} + +func (impl *vcImpl) RunPod(podConfig vc.PodConfig) (*vc.Pod, error) { + return vc.RunPod(podConfig) +} + +func (impl *vcImpl) ListPod() ([]vc.PodStatus, error) { + return vc.ListPod() +} + +func (impl *vcImpl) StatusPod(podID string) (vc.PodStatus, error) { + return vc.StatusPod(podID) +} + +func (impl *vcImpl) PausePod(podID string) (*vc.Pod, error) { + return vc.PausePod(podID) +} + +func (impl *vcImpl) ResumePod(podID string) (*vc.Pod, error) { + return vc.ResumePod(podID) +} + +func (impl *vcImpl) CreateContainer(podID string, containerConfig vc.ContainerConfig) (*vc.Pod, *vc.Container, error) { + return vc.CreateContainer(podID, containerConfig) +} + +func (impl *vcImpl) DeleteContainer(podID, containerID string) (*vc.Container, error) { + return vc.DeleteContainer(podID, containerID) +} + +func (impl *vcImpl) StartContainer(podID, containerID string) (*vc.Container, error) { + return vc.StartContainer(podID, containerID) +} + +func (impl *vcImpl) StopContainer(podID, containerID string) (*vc.Container, error) { + return vc.StopContainer(podID, containerID) +} + +func (impl *vcImpl) EnterContainer(podID, containerID string, cmd vc.Cmd) (*vc.Pod, *vc.Container, *vc.Process, error) { + return vc.EnterContainer(podID, containerID, cmd) +} + +func (impl *vcImpl) StatusContainer(podID, containerID string) (vc.ContainerStatus, error) { + return vc.StatusContainer(podID, containerID) +} + +func (impl *vcImpl) KillContainer(podID, containerID string, signal syscall.Signal, all bool) error { + return vc.KillContainer(podID, containerID, signal, all) +} diff --git a/rvc-implementation_test.go b/rvc-implementation_test.go new file mode 100644 index 00000000..1f9e3ca1 --- /dev/null +++ b/rvc-implementation_test.go @@ -0,0 +1,278 @@ +// Copyright (c) 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Description: The mock implementation of virtcontainers that implements the RVC interface. +// This implementation provides the following behavioural options for all RVC interface functions: +// +// - calling the official virtcontainers API (default). +// - returning an error in a known format (that can be verified by the tests using isMockError()). +// - calling a custom function for more elaborate scenarios. + +package main + +import ( + "fmt" + "strings" + "syscall" + + "github.com/Sirupsen/logrus" + vc "github.com/containers/virtcontainers" +) + +// mockImpl is a virtcontainers implementation type +type mockImpl struct { + // cause all interface functions to fail if set + forceFailure bool + + // Used to override behaviour of particular functions + // (offering more fine-grained control than forceFailure) + setLoggerFunc func(logger logrus.FieldLogger) + createPodFunc func(podConfig vc.PodConfig) (*vc.Pod, error) + deletePodFunc func(podID string) (*vc.Pod, error) + startPodFunc func(podID string) (*vc.Pod, error) + stopPodFunc func(podID string) (*vc.Pod, error) + runPodFunc func(podConfig vc.PodConfig) (*vc.Pod, error) + listPodFunc func() ([]vc.PodStatus, error) + statusPodFunc func(podID string) (vc.PodStatus, error) + pausePodFunc func(podID string) (*vc.Pod, error) + resumePodFunc func(podID string) (*vc.Pod, error) + createContainerFunc func(podID string, containerConfig vc.ContainerConfig) (*vc.Pod, *vc.Container, error) + deleteContainerFunc func(podID, containerID string) (*vc.Container, error) + startContainerFunc func(podID, containerID string) (*vc.Container, error) + stopContainerFunc func(podID, containerID string) (*vc.Container, error) + enterContainerFunc func(podID, containerID string, cmd vc.Cmd) (*vc.Pod, *vc.Container, *vc.Process, error) + statusContainerFunc func(podID, containerID string) (vc.ContainerStatus, error) + killContainerFunc func(podID, containerID string, signal syscall.Signal, all bool) error +} + +// testingImpl is a concrete mock RVC implementation used for testing +var testingImpl = &mockImpl{} + +// mockErrorPrefix is a string that all errors returned by the mock +// implementation itself will contain this string as a prefix. +const mockErrorPrefix = "mockImpl forced failure" + +func init() { + fmt.Printf("INFO: switching to fake virtcontainers implementation for testing\n") + vci = testingImpl +} + +func (impl *mockImpl) SetLogger(logger logrus.FieldLogger) { + if impl.setLoggerFunc != nil { + impl.setLoggerFunc(logger) + return + } + + vc.SetLogger(logger) +} + +func (impl *mockImpl) CreatePod(podConfig vc.PodConfig) (*vc.Pod, error) { + if impl.createPodFunc != nil { + return impl.createPodFunc(podConfig) + } + + if impl.forceFailure { + return nil, fmt.Errorf("%s: CreatePod: podConfig: %v", mockErrorPrefix, podConfig) + } + + return vc.CreatePod(podConfig) +} + +func (impl *mockImpl) DeletePod(podID string) (*vc.Pod, error) { + if impl.deletePodFunc != nil { + return impl.deletePodFunc(podID) + } + + if impl.forceFailure { + return nil, fmt.Errorf("%s: DeletePod: podID: %v", mockErrorPrefix, podID) + } + + return vc.DeletePod(podID) +} + +func (impl *mockImpl) StartPod(podID string) (*vc.Pod, error) { + if impl.startPodFunc != nil { + return impl.startPodFunc(podID) + } + + if impl.forceFailure { + return nil, fmt.Errorf("%s: StartPod: podID: %v", mockErrorPrefix, podID) + } + + return vc.StartPod(podID) +} + +func (impl *mockImpl) StopPod(podID string) (*vc.Pod, error) { + if impl.stopPodFunc != nil { + return impl.stopPodFunc(podID) + } + + if impl.forceFailure { + return nil, fmt.Errorf("%s: StopPod: podID: %v", mockErrorPrefix, podID) + } + + return vc.StopPod(podID) +} + +func (impl *mockImpl) RunPod(podConfig vc.PodConfig) (*vc.Pod, error) { + if impl.runPodFunc != nil { + return impl.runPodFunc(podConfig) + } + + if impl.forceFailure { + return nil, fmt.Errorf("%s: RunPod: podConfig: %v", mockErrorPrefix, podConfig) + } + + return vc.RunPod(podConfig) +} + +func (impl *mockImpl) ListPod() ([]vc.PodStatus, error) { + if impl.listPodFunc != nil { + return impl.listPodFunc() + } + + if impl.forceFailure { + return nil, fmt.Errorf("%s: ListPod", mockErrorPrefix) + } + + return vc.ListPod() +} + +func (impl *mockImpl) StatusPod(podID string) (vc.PodStatus, error) { + if impl.statusPodFunc != nil { + return impl.statusPodFunc(podID) + } + + if impl.forceFailure { + return vc.PodStatus{}, fmt.Errorf("%s: StatusPod: podID: %v", mockErrorPrefix, podID) + } + + return vc.StatusPod(podID) +} + +func (impl *mockImpl) PausePod(podID string) (*vc.Pod, error) { + if impl.pausePodFunc != nil { + return impl.pausePodFunc(podID) + } + + if impl.forceFailure { + return nil, fmt.Errorf("%s: PausePod: podID: %v", mockErrorPrefix, podID) + } + + return vc.PausePod(podID) +} + +func (impl *mockImpl) ResumePod(podID string) (*vc.Pod, error) { + if impl.resumePodFunc != nil { + return impl.resumePodFunc(podID) + } + + if impl.forceFailure { + return nil, fmt.Errorf("%s: ResumePod: podID: %v", mockErrorPrefix, podID) + } + + return vc.ResumePod(podID) +} + +func (impl *mockImpl) CreateContainer(podID string, containerConfig vc.ContainerConfig) (*vc.Pod, *vc.Container, error) { + if impl.createContainerFunc != nil { + return impl.createContainerFunc(podID, containerConfig) + } + + if impl.forceFailure { + return nil, nil, fmt.Errorf("%s: CreateContainer: podID: %v, containerConfig: %v", mockErrorPrefix, podID, containerConfig) + } + + return vc.CreateContainer(podID, containerConfig) +} + +func (impl *mockImpl) DeleteContainer(podID, containerID string) (*vc.Container, error) { + if impl.deleteContainerFunc != nil { + return impl.deleteContainerFunc(podID, containerID) + } + + if impl.forceFailure { + return nil, fmt.Errorf("%s: DeleteContainer: podID: %v, containerID: %v", mockErrorPrefix, podID, containerID) + } + + return vc.DeleteContainer(podID, containerID) +} + +func (impl *mockImpl) StartContainer(podID, containerID string) (*vc.Container, error) { + if impl.startContainerFunc != nil { + return impl.startContainerFunc(podID, containerID) + } + + if impl.forceFailure { + return nil, fmt.Errorf("%s: StartContainer: podID: %v, containerID: %v", mockErrorPrefix, podID, containerID) + } + + return vc.StartContainer(podID, containerID) +} + +func (impl *mockImpl) StopContainer(podID, containerID string) (*vc.Container, error) { + if impl.stopContainerFunc != nil { + return impl.stopContainerFunc(podID, containerID) + } + + if impl.forceFailure { + return nil, fmt.Errorf("%s: StopContainer: podID: %v, containerID: %v", mockErrorPrefix, podID, containerID) + } + + return vc.StopContainer(podID, containerID) +} + +func (impl *mockImpl) EnterContainer(podID, containerID string, cmd vc.Cmd) (*vc.Pod, *vc.Container, *vc.Process, error) { + if impl.enterContainerFunc != nil { + return impl.enterContainerFunc(podID, containerID, cmd) + } + + if impl.forceFailure { + return nil, nil, nil, fmt.Errorf("%s: EnterContainer: podID: %v, containerID: %v, cmd: %v", mockErrorPrefix, podID, containerID, cmd) + } + + return vc.EnterContainer(podID, containerID, cmd) +} + +func (impl *mockImpl) StatusContainer(podID, containerID string) (vc.ContainerStatus, error) { + if impl.statusContainerFunc != nil { + return impl.statusContainerFunc(podID, containerID) + } + + if impl.forceFailure { + return vc.ContainerStatus{}, fmt.Errorf("%s: StatusContainer: podID: %v, containerID: %v", mockErrorPrefix, podID, containerID) + } + + return vc.StatusContainer(podID, containerID) +} + +func (impl *mockImpl) KillContainer(podID, containerID string, signal syscall.Signal, all bool) error { + if impl.killContainerFunc != nil { + return impl.killContainerFunc(podID, containerID, signal, all) + } + + if impl.forceFailure { + return fmt.Errorf("%s: StartContainer: podID: %v, containerID: %v, signal: %v", mockErrorPrefix, podID, containerID, signal) + } + + return vc.KillContainer(podID, containerID, signal, all) +} + +// helper functions +func isMockError(err error) bool { + return strings.HasPrefix(err.Error(), mockErrorPrefix) +} +func listPodNoPods() ([]vc.PodStatus, error) { + return []vc.PodStatus{}, nil +} diff --git a/start.go b/start.go index a8668d03..4cf48661 100644 --- a/start.go +++ b/start.go @@ -63,10 +63,10 @@ func start(containerID string) (*vc.Pod, error) { } if containerType.IsPod() { - return vc.StartPod(podID) + return vci.StartPod(podID) } - c, err := vc.StartContainer(podID, containerID) + c, err := vci.StartContainer(podID, containerID) if err != nil { return nil, err }