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..020ab025 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,213 @@ 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 { + fmt.Printf("DEBUG: createOCIConfig: bundleDir: %v\n", bundleDir) + + 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") + } + + fmt.Printf("DEBUG: createOCIConfig: configCmd: %v\n", configCmd) + + cwd, err := os.Getwd() + if err != nil { + return err + } + + err = os.Chdir(bundleDir) + if err != nil { + return err + } + + defer func() { + err = os.Chdir(cwd) + }() + + output, err := runCommand([]string{configCmd, "spec"}) + fmt.Printf("DEBUG: createOCIConfig: spec err: %v (output %v)\n", err, output) + 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 { + fmt.Printf("DEBUG: makeOCIBundle: bundleDir: %v\n", bundleDir) + fmt.Printf("DEBUG: makeOCIBundle: defaultPauseRootPath: %v\n", defaultPauseRootPath) + + if bundleDir == "" { + return fmt.Errorf("Need bundle directory") + } + + if defaultPauseRootPath == "" { + return fmt.Errorf("BUG: defaultPauseRootPath unset") + } + + // make use of the existing pause bundle + output, err := runCommand([]string{"cp", "-a", defaultPauseRootPath, bundleDir}) + fmt.Printf("DEBUG: makeOCIBundle: output: %v, err: %v\n", output, err) + if err != nil { + return err + } + + err = createOCIConfig(bundleDir) + if err != nil { + return err + } + + //specFile := filepath.Join(bundleDir, "config.json") + //fmt.Printf("DEBUG: makeOCIBundle: specFile: %v\n", specFile) + //spec, err := oci.ParseConfigJSON(specFile) + + // Note the unusual parameter! + spec, err := oci.ParseConfigJSON(bundleDir) + fmt.Printf("DEBUG: makeOCIBundle: spec: %v, err: %v\n", spec, err) + if err != nil { + return err + } + + // Determine the rootfs directory name the OCI config refers to + rootDir := spec.Root.Path + + fmt.Printf("DEBUG: makeOCIBundle: rootDir: %v\n", rootDir) + + //from := defaultPauseRootPath + base := filepath.Base(defaultPauseRootPath) + from := filepath.Join(bundleDir, base) + to := rootDir + + if !strings.HasPrefix(rootDir, "/") { + to = filepath.Join(bundleDir, rootDir) + } + + fmt.Printf("DEBUG: makeOCIBundle: base: %v\n", base) + fmt.Printf("DEBUG: makeOCIBundle: from: %v\n", from) + fmt.Printf("DEBUG: makeOCIBundle: to: %v\n", to) + + output, err = runCommand([]string{"mv", from, to}) + // FIXME: + fmt.Printf("DEBUG: makeOCIBundle: output: %v, err: %v\n", output, err) + 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..f8e7133c --- /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..3f04e6ac --- /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 }