Temporarily remove unit test relying on fake containerd services.

Signed-off-by: Lantao Liu <lantaol@google.com>
This commit is contained in:
Lantao Liu 2017-07-31 22:41:34 +00:00
parent cbd936b734
commit ffb69423ec
16 changed files with 8 additions and 3039 deletions

View File

@ -17,26 +17,14 @@ limitations under the License.
package server
import (
"encoding/json"
"errors"
"os"
"testing"
"github.com/containerd/containerd/api/services/containers"
snapshotapi "github.com/containerd/containerd/api/services/snapshot"
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/net/context"
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing"
servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing"
containerstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/container"
imagestore "github.com/kubernetes-incubator/cri-containerd/pkg/store/image"
sandboxstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/sandbox"
)
func checkMount(t *testing.T, mounts []runtimespec.Mount, src, dest, typ string,
@ -440,159 +428,3 @@ func TestPrivilegedBindMount(t *testing.T) {
}
}
}
func TestCreateContainer(t *testing.T) {
testSandboxID := "test-sandbox-id"
testSandboxPid := uint32(4321)
config, sandboxConfig, imageConfig, specCheck := getCreateContainerTestData()
testSandbox := &sandboxstore.Sandbox{
Metadata: sandboxstore.Metadata{
ID: testSandboxID,
Name: "test-sandbox-name",
Config: sandboxConfig,
Pid: testSandboxPid,
},
}
testContainerName := makeContainerName(config.Metadata, sandboxConfig.Metadata)
// Use an image id to avoid image name resolution.
// TODO(random-liu): Change this to image name after we have complete image
// management unit test framework.
testImageRef := config.GetImage().GetImage()
testChainID := "test-chain-id"
testImage := imagestore.Image{
ID: testImageRef,
ChainID: testChainID,
Config: imageConfig,
}
for desc, test := range map[string]struct {
sandbox *sandboxstore.Sandbox
reserveNameErr bool
imageStoreErr bool
prepareSnapshotErr error
createRootDirErr error
expectErr bool
expectedMeta containerstore.Metadata
}{
"should return error if sandbox does not exist": {
sandbox: nil,
expectErr: true,
},
"should return error if name is reserved": {
sandbox: testSandbox,
reserveNameErr: true,
expectErr: true,
},
"should return error if fail to create root directory": {
sandbox: testSandbox,
createRootDirErr: errors.New("random error"),
expectErr: true,
},
"should return error if image is not pulled": {
sandbox: testSandbox,
imageStoreErr: true,
expectErr: true,
},
"should return error if prepare snapshot fails": {
sandbox: testSandbox,
prepareSnapshotErr: errors.New("random error"),
expectErr: true,
},
"should be able to create container successfully": {
sandbox: testSandbox,
expectErr: false,
expectedMeta: containerstore.Metadata{
Name: testContainerName,
SandboxID: testSandboxID,
ImageRef: testImageRef,
Config: config,
},
},
} {
t.Logf("TestCase %q", desc)
c := newTestCRIContainerdService()
fake := c.containerService.(*servertesting.FakeContainersClient)
fakeSnapshotClient := WithFakeSnapshotClient(c)
fakeOS := c.os.(*ostesting.FakeOS)
if test.sandbox != nil {
assert.NoError(t, c.sandboxStore.Add(*test.sandbox))
}
if test.reserveNameErr {
assert.NoError(t, c.containerNameIndex.Reserve(testContainerName, "random id"))
}
if !test.imageStoreErr {
c.imageStore.Add(testImage)
}
if test.prepareSnapshotErr != nil {
fakeSnapshotClient.InjectError("prepare", test.prepareSnapshotErr)
}
rootExists := false
rootPath := ""
fakeOS.MkdirAllFn = func(path string, perm os.FileMode) error {
assert.Equal(t, os.FileMode(0755), perm)
rootPath = path
if test.createRootDirErr == nil {
rootExists = true
}
return test.createRootDirErr
}
fakeOS.RemoveAllFn = func(path string) error {
assert.Equal(t, rootPath, path)
rootExists = false
return nil
}
resp, err := c.CreateContainer(context.Background(), &runtime.CreateContainerRequest{
PodSandboxId: testSandboxID,
Config: config,
SandboxConfig: sandboxConfig,
})
if test.expectErr {
assert.Error(t, err)
assert.Nil(t, resp)
assert.False(t, rootExists, "root directory should be cleaned up")
if !test.reserveNameErr {
assert.NoError(t, c.containerNameIndex.Reserve(testContainerName, "random id"),
"container name should be released")
}
assert.Empty(t, fakeSnapshotClient.ListMounts(), "snapshot should be cleaned up")
listResp, err := fake.List(context.Background(), &containers.ListContainersRequest{})
assert.NoError(t, err)
assert.Empty(t, listResp.Containers, "containerd container should be cleaned up")
assert.Empty(t, c.containerStore.List(), "container metadata should not be created")
continue
}
assert.NoError(t, err)
assert.NotNil(t, resp)
id := resp.GetContainerId()
assert.True(t, rootExists)
assert.Equal(t, getContainerRootDir(c.rootDir, id), rootPath, "root directory should be created")
// Check runtime spec
containersCalls := fake.GetCalledDetails()
require.Len(t, containersCalls, 1)
createOpts, ok := containersCalls[0].Argument.(*containers.CreateContainerRequest)
assert.True(t, ok, "should create containerd container")
assert.Equal(t, id, createOpts.Container.ID, "container id should be correct")
assert.Equal(t, testImageRef, createOpts.Container.Image, "test image should be correct")
assert.Equal(t, id, createOpts.Container.RootFS, "rootfs should be correct")
spec := &runtimespec.Spec{}
assert.NoError(t, json.Unmarshal(createOpts.Container.Spec.Value, spec))
specCheck(t, id, testSandboxPid, spec)
assert.Equal(t, []string{"prepare"}, fakeSnapshotClient.GetCalledNames(), "prepare should be called")
snapshotCalls := fakeSnapshotClient.GetCalledDetails()
require.Len(t, snapshotCalls, 1)
prepareOpts := snapshotCalls[0].Argument.(*snapshotapi.PrepareRequest)
assert.Equal(t, &snapshotapi.PrepareRequest{
Key: id,
Parent: testChainID,
}, prepareOpts, "prepare request should be correct")
container, err := c.containerStore.Get(id)
assert.NoError(t, err)
test.expectedMeta.ID = id
assert.Equal(t, test.expectedMeta, container.Metadata, "container metadata should be created")
assert.Equal(t, runtime.ContainerState_CONTAINER_CREATED, container.Status.Get().State(),
"container should be in created state")
}
}

View File

@ -17,20 +17,11 @@ limitations under the License.
package server
import (
"errors"
"testing"
"time"
"github.com/containerd/containerd/api/services/containers"
snapshotapi "github.com/containerd/containerd/api/services/snapshot"
"github.com/containerd/containerd/api/types/mount"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing"
servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing"
"github.com/kubernetes-incubator/cri-containerd/pkg/store"
containerstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/container"
)
@ -83,146 +74,3 @@ func TestSetContainerRemoving(t *testing.T) {
}
}
}
func TestRemoveContainer(t *testing.T) {
testID := "test-id"
testName := "test-name"
testContainerStatus := &containerstore.Status{
CreatedAt: time.Now().UnixNano(),
StartedAt: time.Now().UnixNano(),
FinishedAt: time.Now().UnixNano(),
}
for desc, test := range map[string]struct {
status *containerstore.Status
removeSnapshotErr error
deleteContainerErr error
removeDirErr error
expectErr bool
expectUnsetRemoving bool
}{
"should return error when container is still running": {
status: &containerstore.Status{
CreatedAt: time.Now().UnixNano(),
StartedAt: time.Now().UnixNano(),
},
expectErr: true,
},
"should return error when there is ongoing removing": {
status: &containerstore.Status{
CreatedAt: time.Now().UnixNano(),
StartedAt: time.Now().UnixNano(),
FinishedAt: time.Now().UnixNano(),
Removing: true,
},
expectErr: true,
},
"should not return error if container does not exist": {
status: nil,
removeSnapshotErr: servertesting.SnapshotNotExistError,
deleteContainerErr: servertesting.ContainerNotExistError,
expectErr: false,
},
"should not return error if snapshot does not exist": {
status: testContainerStatus,
removeSnapshotErr: servertesting.SnapshotNotExistError,
expectErr: false,
},
"should return error if remove snapshot fails": {
status: testContainerStatus,
removeSnapshotErr: errors.New("random error"),
expectErr: true,
},
"should not return error if containerd container does not exist": {
status: testContainerStatus,
deleteContainerErr: servertesting.ContainerNotExistError,
expectErr: false,
},
"should return error if delete containerd container fails": {
status: testContainerStatus,
deleteContainerErr: errors.New("random error"),
expectErr: true,
},
"should return error if remove container root fails": {
status: testContainerStatus,
removeDirErr: errors.New("random error"),
expectErr: true,
expectUnsetRemoving: true,
},
"should be able to remove container successfully": {
status: testContainerStatus,
expectErr: false,
},
} {
t.Logf("TestCase %q", desc)
c := newTestCRIContainerdService()
fake := c.containerService.(*servertesting.FakeContainersClient)
fakeSnapshotClient := WithFakeSnapshotClient(c)
fakeOS := c.os.(*ostesting.FakeOS)
if test.status != nil {
assert.NoError(t, c.containerNameIndex.Reserve(testName, testID))
container, err := containerstore.NewContainer(
containerstore.Metadata{ID: testID},
*test.status,
)
assert.NoError(t, err)
assert.NoError(t, c.containerStore.Add(container))
}
fakeOS.RemoveAllFn = func(path string) error {
assert.Equal(t, getContainerRootDir(c.rootDir, testID), path)
return test.removeDirErr
}
if test.removeSnapshotErr == nil {
fakeSnapshotClient.SetFakeMounts(testID, []*mount.Mount{
{
Type: "bind",
Source: "/test/source",
Target: "/test/target",
},
})
} else {
fakeSnapshotClient.InjectError("remove", test.removeSnapshotErr)
}
if test.deleteContainerErr == nil {
_, err := fake.Create(context.Background(), &containers.CreateContainerRequest{
Container: containers.Container{ID: testID},
})
assert.NoError(t, err)
} else {
fake.InjectError("delete", test.deleteContainerErr)
}
resp, err := c.RemoveContainer(context.Background(), &runtime.RemoveContainerRequest{
ContainerId: testID,
})
if test.expectErr {
assert.Error(t, err)
assert.Nil(t, resp)
if !test.expectUnsetRemoving {
continue
}
container, err := c.containerStore.Get(testID)
assert.NoError(t, err)
// Also covers resetContainerRemoving.
assert.False(t, container.Status.Get().Removing, "removing state should be unset")
continue
}
assert.NoError(t, err)
assert.NotNil(t, resp)
_, err = c.containerStore.Get(testID)
assert.Equal(t, store.ErrNotExist, err)
assert.NoError(t, c.containerNameIndex.Reserve(testName, testID),
"container name should be released")
mountsResp, err := fakeSnapshotClient.Mounts(context.Background(), &snapshotapi.MountsRequest{Key: testID})
assert.Equal(t, servertesting.SnapshotNotExistError, err, "snapshot should be removed")
assert.Nil(t, mountsResp)
getResp, err := fake.Get(context.Background(), &containers.GetContainerRequest{ID: testID})
assert.Equal(t, servertesting.ContainerNotExistError, err, "containerd container should be removed")
assert.Nil(t, getResp)
resp, err = c.RemoveContainer(context.Background(), &runtime.RemoveContainerRequest{
ContainerId: testID,
})
assert.NoError(t, err)
assert.NotNil(t, resp, "remove should be idempotent")
}
}

View File

@ -1,255 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
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.
*/
package server
import (
"errors"
"io"
"os"
"testing"
"time"
"github.com/containerd/containerd/api/services/execution"
"github.com/containerd/containerd/api/types/mount"
"github.com/containerd/containerd/api/types/task"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing"
servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing"
containerstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/container"
sandboxstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/sandbox"
)
func TestStartContainer(t *testing.T) {
testID := "test-id"
testSandboxID := "test-sandbox-id"
testMetadata := containerstore.Metadata{
ID: testID,
Name: "test-name",
SandboxID: testSandboxID,
}
testStatus := &containerstore.Status{CreatedAt: time.Now().UnixNano()}
testSandbox := &sandboxstore.Sandbox{
Metadata: sandboxstore.Metadata{
ID: testSandboxID,
Name: "test-sandbox-name",
},
}
testSandboxContainer := &task.Task{
ID: testSandboxID,
Pid: uint32(4321),
Status: task.StatusRunning,
}
testMounts := []*mount.Mount{{Type: "bind", Source: "test-source"}}
for desc, test := range map[string]struct {
status *containerstore.Status
sandbox *sandboxstore.Sandbox
sandboxContainerdContainer *task.Task
snapshotMountsErr bool
prepareFIFOErr error
createContainerErr error
startContainerErr error
expectStateChange bool
expectCalls []string
expectErr bool
}{
"should return error when container does not exist": {
status: nil,
sandbox: testSandbox,
sandboxContainerdContainer: testSandboxContainer,
expectCalls: []string{},
expectErr: true,
},
"should return error when container is not in created state": {
status: &containerstore.Status{
CreatedAt: time.Now().UnixNano(),
StartedAt: time.Now().UnixNano(),
},
sandbox: testSandbox,
sandboxContainerdContainer: testSandboxContainer,
expectCalls: []string{},
expectErr: true,
},
"should return error when container is in removing state": {
status: &containerstore.Status{
CreatedAt: time.Now().UnixNano(),
Removing: true,
},
sandbox: testSandbox,
sandboxContainerdContainer: testSandboxContainer,
expectCalls: []string{},
expectErr: true,
},
"should return error when sandbox does not exist": {
status: testStatus,
sandbox: nil,
sandboxContainerdContainer: testSandboxContainer,
expectStateChange: true,
expectCalls: []string{},
expectErr: true,
},
"should return error when sandbox is not running": {
status: testStatus,
sandbox: testSandbox,
sandboxContainerdContainer: &task.Task{
ID: testSandboxID,
Pid: uint32(4321),
Status: task.StatusStopped,
},
expectStateChange: true,
expectCalls: []string{"info"},
expectErr: true,
},
"should return error when snapshot mounts fails": {
status: testStatus,
sandbox: testSandbox,
sandboxContainerdContainer: testSandboxContainer,
snapshotMountsErr: true,
expectStateChange: true,
expectCalls: []string{"info"},
expectErr: true,
},
"should return error when fail to open streaming pipes": {
status: testStatus,
sandbox: testSandbox,
sandboxContainerdContainer: testSandboxContainer,
prepareFIFOErr: errors.New("open error"),
expectStateChange: true,
expectCalls: []string{"info"},
expectErr: true,
},
"should return error when fail to create container": {
status: testStatus,
sandbox: testSandbox,
sandboxContainerdContainer: testSandboxContainer,
createContainerErr: errors.New("create error"),
expectStateChange: true,
expectCalls: []string{"info", "create"},
expectErr: true,
},
"should return error when fail to start container": {
status: testStatus,
sandbox: testSandbox,
sandboxContainerdContainer: testSandboxContainer,
startContainerErr: errors.New("start error"),
expectStateChange: true,
// cleanup the containerd task.
expectCalls: []string{"info", "create", "start", "delete"},
expectErr: true,
},
"should be able to start container successfully": {
status: testStatus,
sandbox: testSandbox,
sandboxContainerdContainer: testSandboxContainer,
expectStateChange: true,
expectCalls: []string{"info", "create", "start"},
expectErr: false,
},
} {
t.Logf("TestCase %q", desc)
c := newTestCRIContainerdService()
fake := c.taskService.(*servertesting.FakeExecutionClient)
fakeOS := c.os.(*ostesting.FakeOS)
fakeSnapshotClient := WithFakeSnapshotClient(c)
if test.status != nil {
cntr, err := containerstore.NewContainer(
testMetadata,
*test.status,
)
assert.NoError(t, err)
assert.NoError(t, c.containerStore.Add(cntr))
}
if test.sandbox != nil {
assert.NoError(t, c.sandboxStore.Add(*test.sandbox))
}
if test.sandboxContainerdContainer != nil {
fake.SetFakeTasks([]task.Task{*test.sandboxContainerdContainer})
}
if !test.snapshotMountsErr {
fakeSnapshotClient.SetFakeMounts(testID, testMounts)
}
// TODO(random-liu): Test behavior with different streaming config.
fakeOS.OpenFifoFn = func(context.Context, string, int, os.FileMode) (io.ReadWriteCloser, error) {
return nopReadWriteCloser{}, test.prepareFIFOErr
}
if test.createContainerErr != nil {
fake.InjectError("create", test.createContainerErr)
}
if test.startContainerErr != nil {
fake.InjectError("start", test.startContainerErr)
}
resp, err := c.StartContainer(context.Background(), &runtime.StartContainerRequest{
ContainerId: testID,
})
// Check containerd functions called.
assert.Equal(t, test.expectCalls, fake.GetCalledNames())
// Check results returned.
if test.expectErr {
assert.Error(t, err)
assert.Nil(t, resp)
} else {
assert.NoError(t, err)
assert.NotNil(t, resp)
}
// Skip following validation if no container is injected initially.
if test.status == nil {
continue
}
// Check container state.
cntr, err := c.containerStore.Get(testID)
assert.NoError(t, err)
status := cntr.Status.Get()
if !test.expectStateChange {
assert.Equal(t, testMetadata, cntr.Metadata)
assert.Equal(t, *test.status, status)
continue
}
if test.expectErr {
t.Logf("container state should be in exited state when fail to start")
assert.Equal(t, runtime.ContainerState_CONTAINER_EXITED, status.State())
assert.Zero(t, status.Pid)
assert.EqualValues(t, errorStartExitCode, status.ExitCode)
assert.Equal(t, errorStartReason, status.Reason)
assert.NotEmpty(t, status.Message)
_, err := fake.Info(context.Background(), &execution.InfoRequest{ContainerID: testID})
assert.True(t, isContainerdGRPCNotFoundError(err),
"containerd task should be cleaned up when fail to start")
continue
}
t.Logf("container state should be running when start successfully")
assert.Equal(t, runtime.ContainerState_CONTAINER_RUNNING, status.State())
info, err := fake.Info(context.Background(), &execution.InfoRequest{ContainerID: testID})
assert.NoError(t, err)
pid := info.Task.Pid
assert.Equal(t, pid, status.Pid)
assert.Equal(t, task.StatusRunning, info.Task.Status)
// Check runtime spec
calls := fake.GetCalledDetails()
createOpts, ok := calls[1].Argument.(*execution.CreateRequest)
assert.True(t, ok, "2nd call should be create")
_, stdout, stderr := getStreamingPipes(getContainerRootDir(c.rootDir, testID))
assert.Equal(t, &execution.CreateRequest{
ContainerID: testID,
Rootfs: testMounts,
// TODO(random-liu): Test streaming configurations.
Stdout: stdout,
Stderr: stderr,
}, createOpts, "create options should be correct")
}
}

View File

@ -17,21 +17,13 @@ limitations under the License.
package server
import (
"errors"
"testing"
"time"
"github.com/containerd/containerd/api/services/execution"
"github.com/containerd/containerd/api/types/task"
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
"golang.org/x/sys/unix"
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing"
containerstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/container"
imagestore "github.com/kubernetes-incubator/cri-containerd/pkg/store/image"
)
func TestWaitContainerStop(t *testing.T) {
@ -93,240 +85,3 @@ func TestWaitContainerStop(t *testing.T) {
assert.Equal(t, test.expectErr, err != nil, desc)
}
}
func TestStopContainer(t *testing.T) {
testID := "test-id"
testPid := uint32(1234)
testImageID := "test-image-id"
testStatus := containerstore.Status{
Pid: testPid,
CreatedAt: time.Now().UnixNano(),
StartedAt: time.Now().UnixNano(),
}
testImage := imagestore.Image{
ID: testImageID,
Config: &imagespec.ImageConfig{},
}
testContainer := task.Task{
ID: testID,
Pid: testPid,
Status: task.StatusRunning,
}
for desc, test := range map[string]struct {
status *containerstore.Status
containerdContainer *task.Task
stopSignal string
stopErr error
noTimeout bool
expectErr bool
expectCalls []servertesting.CalledDetail
}{
"should return error when container does not exist": {
status: nil,
expectErr: true,
expectCalls: []servertesting.CalledDetail{},
},
"should not return error when container is not running": {
status: &containerstore.Status{CreatedAt: time.Now().UnixNano()},
expectErr: false,
expectCalls: []servertesting.CalledDetail{},
},
"should not return error if containerd task does not exist": {
status: &testStatus,
containerdContainer: &testContainer,
// Since it's hard to inject event during `StopContainer` is running,
// we only test the case that first stop returns error, but container
// status is not updated yet.
// We also leverage this behavior to test that when graceful
// stop doesn't take effect, container should be SIGKILL-ed.
stopErr: servertesting.TaskNotExistError,
expectErr: false,
expectCalls: []servertesting.CalledDetail{
{
Name: "kill",
Argument: &execution.KillRequest{
ContainerID: testID,
Signal: uint32(unix.SIGTERM),
PidOrAll: &execution.KillRequest_All{All: true},
},
},
{
Name: "kill",
Argument: &execution.KillRequest{
ContainerID: testID,
Signal: uint32(unix.SIGKILL),
PidOrAll: &execution.KillRequest_All{All: true},
},
},
{
Name: "delete",
Argument: &execution.DeleteRequest{ContainerID: testID},
},
},
},
"should not return error if containerd task process already finished": {
status: &testStatus,
containerdContainer: &testContainer,
stopErr: errors.New("os: process already finished"),
expectErr: false,
expectCalls: []servertesting.CalledDetail{
{
Name: "kill",
Argument: &execution.KillRequest{
ContainerID: testID,
Signal: uint32(unix.SIGTERM),
PidOrAll: &execution.KillRequest_All{All: true},
},
},
{
Name: "kill",
Argument: &execution.KillRequest{
ContainerID: testID,
Signal: uint32(unix.SIGKILL),
PidOrAll: &execution.KillRequest_All{All: true},
},
},
{
Name: "delete",
Argument: &execution.DeleteRequest{ContainerID: testID},
},
},
},
"should return error if graceful stop returns random error": {
status: &testStatus,
containerdContainer: &testContainer,
stopErr: errors.New("random stop error"),
expectErr: true,
expectCalls: []servertesting.CalledDetail{
{
Name: "kill",
Argument: &execution.KillRequest{
ContainerID: testID,
Signal: uint32(unix.SIGTERM),
PidOrAll: &execution.KillRequest_All{All: true},
},
},
},
},
"should not return error if containerd task is gracefully stopped": {
status: &testStatus,
containerdContainer: &testContainer,
expectErr: false,
// deleted by the event monitor.
expectCalls: []servertesting.CalledDetail{
{
Name: "kill",
Argument: &execution.KillRequest{
ContainerID: testID,
Signal: uint32(unix.SIGTERM),
PidOrAll: &execution.KillRequest_All{All: true},
},
},
{
Name: "delete",
Argument: &execution.DeleteRequest{ContainerID: testID},
},
},
},
"should use stop signal specified in image config if not empty": {
status: &testStatus,
containerdContainer: &testContainer,
stopSignal: "SIGHUP",
expectErr: false,
// deleted by the event monitor.
expectCalls: []servertesting.CalledDetail{
{
Name: "kill",
Argument: &execution.KillRequest{
ContainerID: testID,
Signal: uint32(unix.SIGHUP),
PidOrAll: &execution.KillRequest_All{All: true},
},
},
{
Name: "delete",
Argument: &execution.DeleteRequest{ContainerID: testID},
},
},
},
"should directly kill container if timeout is 0": {
status: &testStatus,
containerdContainer: &testContainer,
noTimeout: true,
expectErr: false,
expectCalls: []servertesting.CalledDetail{
{
Name: "kill",
Argument: &execution.KillRequest{
ContainerID: testID,
Signal: uint32(unix.SIGKILL),
PidOrAll: &execution.KillRequest_All{All: true},
},
},
{
Name: "delete",
Argument: &execution.DeleteRequest{ContainerID: testID},
},
},
},
// TODO(random-liu): Test "should return error if both failed" after we have
// fake clock for test.
} {
t.Logf("TestCase %q", desc)
c := newTestCRIContainerdService()
fake := servertesting.NewFakeExecutionClient().WithEvents()
defer fake.Stop()
c.taskService = fake
// Inject the container.
if test.status != nil {
cntr, err := containerstore.NewContainer(
containerstore.Metadata{
ID: testID,
ImageRef: testImageID,
},
*test.status,
)
assert.NoError(t, err)
assert.NoError(t, c.containerStore.Add(cntr))
}
// Inject containerd task.
if test.containerdContainer != nil {
fake.SetFakeTasks([]task.Task{*test.containerdContainer})
}
testImage.Config.StopSignal = test.stopSignal
c.imageStore.Add(testImage)
if test.stopErr != nil {
fake.InjectError("kill", test.stopErr)
}
eventClient, err := fake.Events(context.Background(), &execution.EventsRequest{})
assert.NoError(t, err)
// Start a simple test event monitor.
go func(e execution.Tasks_EventsClient) {
for {
if err := c.handleEventStream(e); err != nil { // nolint: vetshadow
return
}
}
}(eventClient)
fake.ClearCalls()
timeout := int64(1)
if test.noTimeout {
timeout = 0
}
// 1 second timeout should be enough for the unit test.
// TODO(random-liu): Use fake clock for this test.
resp, err := c.StopContainer(context.Background(), &runtime.StopContainerRequest{
ContainerId: testID,
Timeout: timeout,
})
if test.expectErr {
assert.Error(t, err)
assert.Nil(t, resp)
} else {
assert.NoError(t, err)
assert.NotNil(t, resp)
}
assert.Equal(t, test.expectCalls, fake.GetCalledDetails())
}
}

View File

@ -1,172 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
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.
*/
package server
import (
"fmt"
"testing"
"time"
"github.com/containerd/containerd/api/services/execution"
"github.com/containerd/containerd/api/types/task"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing"
containerstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/container"
)
func TestHandleEvent(t *testing.T) {
testID := "test-id"
testPid := uint32(1234)
testCreatedAt := time.Now().UnixNano()
testStartedAt := time.Now().UnixNano()
testMetadata := containerstore.Metadata{
ID: testID,
Name: "test-name",
SandboxID: "test-sandbox-id",
}
// Container status in running state.
testStatus := containerstore.Status{
Pid: testPid,
CreatedAt: testCreatedAt,
StartedAt: testStartedAt,
}
testExitedAt := time.Now()
testExitEvent := task.Event{
ID: testID,
Type: task.Event_EXIT,
Pid: testPid,
ExitStatus: 1,
ExitedAt: testExitedAt,
}
testFinishedStatus := containerstore.Status{
Pid: 0,
CreatedAt: testCreatedAt,
StartedAt: testStartedAt,
FinishedAt: testExitedAt.UnixNano(),
ExitCode: 1,
}
assert.Equal(t, runtime.ContainerState_CONTAINER_RUNNING, testStatus.State())
testContainerdContainer := task.Task{
ID: testID,
Pid: testPid,
Status: task.StatusRunning,
}
for desc, test := range map[string]struct {
event *task.Event
status *containerstore.Status
containerdContainer *task.Task
containerdErr error
expected *containerstore.Status
}{
"should not update state when no corresponding container for event": {
event: &testExitEvent,
expected: nil,
},
"should not update state when exited process is not init process": {
event: &task.Event{
ID: testID,
Type: task.Event_EXIT,
Pid: 9999,
ExitStatus: 1,
ExitedAt: testExitedAt,
},
status: &testStatus,
containerdContainer: &testContainerdContainer,
expected: &testStatus,
},
"should not update state when fail to delete containerd task": {
event: &testExitEvent,
status: &testStatus,
containerdContainer: &testContainerdContainer,
containerdErr: fmt.Errorf("random error"),
expected: &testStatus,
},
"should not update state for irrelevant events": {
event: &task.Event{
ID: testID,
Type: task.Event_PAUSED,
Pid: testPid,
},
status: &testStatus,
containerdContainer: &testContainerdContainer,
expected: &testStatus,
},
"should update state when containerd task is already deleted": {
event: &testExitEvent,
status: &testStatus,
expected: &testFinishedStatus,
},
"should update state when delete containerd task successfully": {
event: &testExitEvent,
status: &testStatus,
containerdContainer: &testContainerdContainer,
expected: &testFinishedStatus,
},
"should update exit reason when container is oom killed": {
event: &task.Event{
ID: testID,
Type: task.Event_OOM,
},
status: &testStatus,
expected: &containerstore.Status{
Pid: testPid,
CreatedAt: testCreatedAt,
StartedAt: testStartedAt,
Reason: oomExitReason,
},
},
} {
t.Logf("TestCase %q", desc)
c := newTestCRIContainerdService()
fake := c.taskService.(*servertesting.FakeExecutionClient)
e, err := fake.Events(context.Background(), &execution.EventsRequest{})
assert.NoError(t, err)
fakeEvents := e.(*servertesting.EventClient)
// Inject event.
if test.event != nil {
fakeEvents.Events <- test.event
}
// Inject internal container object.
if test.status != nil {
cntr, err := containerstore.NewContainer( // nolint: vetshadow
testMetadata,
*test.status,
)
assert.NoError(t, err)
assert.NoError(t, c.containerStore.Add(cntr))
}
// Inject containerd task.
if test.containerdContainer != nil {
fake.SetFakeTasks([]task.Task{*test.containerdContainer})
}
// Inject containerd delete error.
if test.containerdErr != nil {
fake.InjectError("delete", test.containerdErr)
}
assert.NoError(t, c.handleEventStream(e))
if test.expected == nil {
continue
}
got, err := c.containerStore.Get(testID)
assert.NoError(t, err)
assert.Equal(t, *test.expected, got.Status.Get())
}
}

View File

@ -21,13 +21,8 @@ import (
"time"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
"github.com/containerd/containerd/api/types/task"
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing"
sandboxstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/sandbox"
)
@ -131,86 +126,3 @@ func TestFilterSandboxes(t *testing.T) {
assert.Equal(t, test.expect, filtered, desc)
}
}
func TestListPodSandbox(t *testing.T) {
c := newTestCRIContainerdService()
fake := c.taskService.(*servertesting.FakeExecutionClient)
sandboxesInStore := []sandboxstore.Sandbox{
{
Metadata: sandboxstore.Metadata{
ID: "1",
Name: "name-1",
Config: &runtime.PodSandboxConfig{Metadata: &runtime.PodSandboxMetadata{Name: "name-1"}},
},
},
{
Metadata: sandboxstore.Metadata{
ID: "2",
Name: "name-2",
Config: &runtime.PodSandboxConfig{Metadata: &runtime.PodSandboxMetadata{Name: "name-2"}},
},
},
{
Metadata: sandboxstore.Metadata{
ID: "3",
Name: "name-3",
Config: &runtime.PodSandboxConfig{Metadata: &runtime.PodSandboxMetadata{Name: "name-3"}},
},
},
}
sandboxesInContainerd := []task.Task{
// Running container with corresponding metadata
{
ID: "1",
Pid: 1,
Status: task.StatusRunning,
},
// Stopped container with corresponding metadata
{
ID: "2",
Pid: 2,
Status: task.StatusStopped,
},
// Container without corresponding metadata
{
ID: "4",
Pid: 4,
Status: task.StatusStopped,
},
}
expect := []*runtime.PodSandbox{
{
Id: "1",
Metadata: &runtime.PodSandboxMetadata{Name: "name-1"},
State: runtime.PodSandboxState_SANDBOX_READY,
},
{
Id: "2",
Metadata: &runtime.PodSandboxMetadata{Name: "name-2"},
State: runtime.PodSandboxState_SANDBOX_NOTREADY,
},
{
Id: "3",
Metadata: &runtime.PodSandboxMetadata{Name: "name-3"},
State: runtime.PodSandboxState_SANDBOX_NOTREADY,
},
}
// Inject test metadata
for _, s := range sandboxesInStore {
assert.NoError(t, c.sandboxStore.Add(s))
}
// Inject fake containerd tasks
fake.SetFakeTasks(sandboxesInContainerd)
resp, err := c.ListPodSandbox(context.Background(), &runtime.ListPodSandboxRequest{})
assert.NoError(t, err)
sandboxes := resp.GetItems()
assert.Len(t, sandboxes, len(expect))
for _, s := range expect {
assert.Contains(t, sandboxes, s)
}
}

View File

@ -1,241 +0,0 @@
/* Copyright 2017 The Kubernetes Authors.
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.
*/
package server
import (
"fmt"
"testing"
"time"
"github.com/containerd/containerd/api/services/containers"
snapshotapi "github.com/containerd/containerd/api/services/snapshot"
"github.com/containerd/containerd/api/types/mount"
"github.com/containerd/containerd/api/types/task"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing"
servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing"
"github.com/kubernetes-incubator/cri-containerd/pkg/store"
containerstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/container"
sandboxstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/sandbox"
)
func TestRemovePodSandbox(t *testing.T) {
testID := "test-id"
testName := "test-name"
testSandbox := sandboxstore.Sandbox{
Metadata: sandboxstore.Metadata{
ID: testID,
Name: testName,
},
}
for desc, test := range map[string]struct {
sandboxTasks []task.Task
injectSandbox bool
removeSnapshotErr error
deleteContainerErr error
taskInfoErr error
injectFSErr error
expectErr bool
expectRemoved string
expectCalls []string
}{
"should not return error if sandbox does not exist": {
injectSandbox: false,
removeSnapshotErr: servertesting.SnapshotNotExistError,
deleteContainerErr: servertesting.ContainerNotExistError,
expectErr: false,
expectCalls: []string{},
},
"should not return error if snapshot does not exist": {
injectSandbox: true,
removeSnapshotErr: servertesting.SnapshotNotExistError,
expectRemoved: getSandboxRootDir(testRootDir, testID),
expectCalls: []string{"info"},
},
"should return error if remove snapshot fails": {
injectSandbox: true,
removeSnapshotErr: fmt.Errorf("arbitrary error"),
expectErr: true,
expectCalls: []string{"info"},
},
"should return error when sandbox container task is not deleted": {
injectSandbox: true,
sandboxTasks: []task.Task{{ID: testID}},
expectErr: true,
expectCalls: []string{"info"},
},
"should return error when arbitrary containerd error is injected": {
injectSandbox: true,
taskInfoErr: fmt.Errorf("arbitrary error"),
expectErr: true,
expectCalls: []string{"info"},
},
"should return error when error fs error is injected": {
injectSandbox: true,
injectFSErr: fmt.Errorf("fs error"),
expectRemoved: getSandboxRootDir(testRootDir, testID),
expectErr: true,
expectCalls: []string{"info"},
},
"should not return error if sandbox container does not exist": {
injectSandbox: true,
deleteContainerErr: servertesting.ContainerNotExistError,
expectRemoved: getSandboxRootDir(testRootDir, testID),
expectCalls: []string{"info"},
},
"should return error if delete sandbox container fails": {
injectSandbox: true,
deleteContainerErr: fmt.Errorf("arbitrary error"),
expectRemoved: getSandboxRootDir(testRootDir, testID),
expectErr: true,
expectCalls: []string{"info"},
},
"should be able to successfully delete": {
injectSandbox: true,
expectRemoved: getSandboxRootDir(testRootDir, testID),
expectCalls: []string{"info"},
},
} {
t.Logf("TestCase %q", desc)
c := newTestCRIContainerdService()
fake := c.containerService.(*servertesting.FakeContainersClient)
fakeOS := c.os.(*ostesting.FakeOS)
fakeExecutionClient := c.taskService.(*servertesting.FakeExecutionClient)
fakeSnapshotClient := WithFakeSnapshotClient(c)
fakeExecutionClient.SetFakeTasks(test.sandboxTasks)
if test.injectSandbox {
c.sandboxNameIndex.Reserve(testName, testID)
assert.NoError(t, c.sandboxStore.Add(testSandbox))
}
if test.removeSnapshotErr == nil {
fakeSnapshotClient.SetFakeMounts(testID, []*mount.Mount{
{
Type: "bind",
Source: "/test/source",
Target: "/test/target",
},
})
} else {
fakeSnapshotClient.InjectError("remove", test.removeSnapshotErr)
}
if test.deleteContainerErr == nil {
_, err := fake.Create(context.Background(), &containers.CreateContainerRequest{
Container: containers.Container{ID: testID},
})
assert.NoError(t, err)
} else {
fake.InjectError("delete", test.deleteContainerErr)
}
if test.taskInfoErr != nil {
fakeExecutionClient.InjectError("info", test.taskInfoErr)
}
fakeOS.RemoveAllFn = func(path string) error {
assert.Equal(t, test.expectRemoved, path)
return test.injectFSErr
}
res, err := c.RemovePodSandbox(context.Background(), &runtime.RemovePodSandboxRequest{
PodSandboxId: testID,
})
assert.Equal(t, test.expectCalls, fakeExecutionClient.GetCalledNames())
if test.expectErr {
assert.Error(t, err)
assert.Nil(t, res)
continue
}
assert.NoError(t, err)
assert.NotNil(t, res)
assert.NoError(t, c.sandboxNameIndex.Reserve(testName, testID),
"sandbox name should be released")
_, err = c.sandboxStore.Get(testID)
assert.Equal(t, store.ErrNotExist, err, "sandbox should be removed")
mountsResp, err := fakeSnapshotClient.Mounts(context.Background(), &snapshotapi.MountsRequest{Key: testID})
assert.Equal(t, servertesting.SnapshotNotExistError, err, "snapshot should be removed")
assert.Nil(t, mountsResp)
getResp, err := fake.Get(context.Background(), &containers.GetContainerRequest{ID: testID})
assert.Equal(t, servertesting.ContainerNotExistError, err, "containerd container should be removed")
assert.Nil(t, getResp)
res, err = c.RemovePodSandbox(context.Background(), &runtime.RemovePodSandboxRequest{
PodSandboxId: testID,
})
assert.NoError(t, err)
assert.NotNil(t, res, "remove should be idempotent")
}
}
func TestRemoveContainersInSandbox(t *testing.T) {
testID := "test-id"
testName := "test-name"
testSandbox := sandboxstore.Sandbox{
Metadata: sandboxstore.Metadata{
ID: testID,
Name: testName,
},
}
testContainers := []containerForTest{
{
metadata: containerstore.Metadata{
ID: "test-cid-1",
Name: "test-cname-1",
SandboxID: testID,
},
status: containerstore.Status{FinishedAt: time.Now().UnixNano()},
},
{
metadata: containerstore.Metadata{
ID: "test-cid-2",
Name: "test-cname-2",
SandboxID: testID,
},
status: containerstore.Status{FinishedAt: time.Now().UnixNano()},
},
{
metadata: containerstore.Metadata{
ID: "test-cid-3",
Name: "test-cname-3",
SandboxID: "other-sandbox-id",
},
status: containerstore.Status{FinishedAt: time.Now().UnixNano()},
},
}
c := newTestCRIContainerdService()
WithFakeSnapshotClient(c)
assert.NoError(t, c.sandboxNameIndex.Reserve(testName, testID))
assert.NoError(t, c.sandboxStore.Add(testSandbox))
for _, tc := range testContainers {
assert.NoError(t, c.containerNameIndex.Reserve(tc.metadata.Name, tc.metadata.ID))
cntr, err := tc.toContainer()
assert.NoError(t, err)
assert.NoError(t, c.containerStore.Add(cntr))
}
res, err := c.RemovePodSandbox(context.Background(), &runtime.RemovePodSandboxRequest{
PodSandboxId: testID,
})
assert.NoError(t, err)
assert.NotNil(t, res)
_, err = c.sandboxStore.Get(testID)
assert.Equal(t, store.ErrNotExist, err, "sandbox metadata should be removed")
cntrs := c.containerStore.List()
assert.Len(t, cntrs, 1)
assert.Equal(t, testContainers[2].metadata, cntrs[0].Metadata, "container should be removed")
assert.Equal(t, testContainers[2].status, cntrs[0].Status.Get(), "container should be removed")
}

View File

@ -17,25 +17,16 @@ limitations under the License.
package server
import (
"encoding/json"
"io"
"os"
"syscall"
"testing"
"github.com/containerd/containerd/api/services/containers"
"github.com/containerd/containerd/api/services/execution"
snapshotapi "github.com/containerd/containerd/api/services/snapshot"
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/net/context"
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing"
servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing"
imagestore "github.com/kubernetes-incubator/cri-containerd/pkg/store/image"
)
func getRunPodSandboxTestData() (*runtime.PodSandboxConfig, *imagespec.ImageConfig, func(*testing.T, string, *runtimespec.Spec)) {
@ -269,112 +260,6 @@ options timeout:1
}
}
func TestRunPodSandbox(t *testing.T) {
config, imageConfig, specCheck := getRunPodSandboxTestData()
c := newTestCRIContainerdService()
fake := c.containerService.(*servertesting.FakeContainersClient)
fakeSnapshotClient := WithFakeSnapshotClient(c)
fakeExecutionClient := c.taskService.(*servertesting.FakeExecutionClient)
fakeCNIPlugin := c.netPlugin.(*servertesting.FakeCNIPlugin)
fakeOS := c.os.(*ostesting.FakeOS)
var dirs []string
var pipes []string
fakeOS.MkdirAllFn = func(path string, perm os.FileMode) error {
dirs = append(dirs, path)
return nil
}
fakeOS.OpenFifoFn = func(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
pipes = append(pipes, fn)
assert.Equal(t, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, flag)
assert.Equal(t, os.FileMode(0700), perm)
return nopReadWriteCloser{}, nil
}
testChainID := "test-sandbox-chain-id"
image := imagestore.Image{
ID: testSandboxImage,
ChainID: testChainID,
Config: imageConfig,
}
// Insert sandbox image.
c.imageStore.Add(image)
expectContainersClientCalls := []string{"create"}
expectSnapshotClientCalls := []string{"view"}
expectExecutionClientCalls := []string{"create", "start"}
res, err := c.RunPodSandbox(context.Background(), &runtime.RunPodSandboxRequest{Config: config})
assert.NoError(t, err)
require.NotNil(t, res)
id := res.GetPodSandboxId()
sandboxRootDir := getSandboxRootDir(c.rootDir, id)
assert.Contains(t, dirs[0], sandboxRootDir, "sandbox root directory should be created")
assert.Len(t, pipes, 2)
_, stdout, stderr := getStreamingPipes(sandboxRootDir)
assert.Contains(t, pipes, stdout, "sandbox stdout pipe should be created")
assert.Contains(t, pipes, stderr, "sandbox stderr pipe should be created")
assert.Equal(t, expectSnapshotClientCalls, fakeSnapshotClient.GetCalledNames(), "expect snapshot functions should be called")
calls := fakeSnapshotClient.GetCalledDetails()
prepareOpts := calls[0].Argument.(*snapshotapi.PrepareRequest)
assert.Equal(t, &snapshotapi.PrepareRequest{
Key: id,
Parent: testChainID,
}, prepareOpts, "prepare request should be correct")
assert.Equal(t, expectContainersClientCalls, fake.GetCalledNames(), "expect containers functions should be called")
calls = fake.GetCalledDetails()
createOpts, ok := calls[0].Argument.(*containers.CreateContainerRequest)
assert.True(t, ok, "should create sandbox container")
assert.Equal(t, id, createOpts.Container.ID, "container id should be correct")
assert.Equal(t, testSandboxImage, createOpts.Container.Image, "test image should be correct")
assert.Equal(t, id, createOpts.Container.RootFS, "rootfs should be correct")
spec := &runtimespec.Spec{}
assert.NoError(t, json.Unmarshal(createOpts.Container.Spec.Value, spec))
t.Logf("oci spec check")
specCheck(t, id, spec)
assert.Equal(t, expectExecutionClientCalls, fakeExecutionClient.GetCalledNames(), "expect execution functions should be called")
calls = fakeExecutionClient.GetCalledDetails()
createTaskOpts := calls[0].Argument.(*execution.CreateRequest)
mountsResp, err := fakeSnapshotClient.Mounts(context.Background(), &snapshotapi.MountsRequest{Key: id})
assert.NoError(t, err)
assert.Equal(t, &execution.CreateRequest{
ContainerID: id,
Rootfs: mountsResp.Mounts,
Stdout: stdout,
Stderr: stderr,
}, createTaskOpts, "create options should be correct")
startID := calls[1].Argument.(*execution.StartRequest).ContainerID
assert.Equal(t, id, startID, "start id should be correct")
sandbox, err := c.sandboxStore.Get(id)
assert.NoError(t, err)
assert.Equal(t, id, sandbox.ID, "sandbox id should be correct")
err = c.sandboxNameIndex.Reserve(sandbox.Name, "random-id")
assert.Error(t, err, "sandbox name should be reserved")
assert.Equal(t, config, sandbox.Config, "sandbox config should be correct")
// TODO(random-liu): [P2] Add clock interface and use fake clock.
assert.NotZero(t, sandbox.CreatedAt, "sandbox CreatedAt should be set")
info, err := fakeExecutionClient.Info(context.Background(), &execution.InfoRequest{ContainerID: id})
assert.NoError(t, err)
pid := info.Task.Pid
assert.Equal(t, sandbox.NetNS, getNetworkNamespace(pid), "sandbox network namespace should be correct")
expectedCNICalls := []string{"SetUpPod"}
assert.Equal(t, expectedCNICalls, fakeCNIPlugin.GetCalledNames(), "expect SetUpPod should be called")
calls = fakeCNIPlugin.GetCalledDetails()
pluginArgument := calls[0].Argument.(servertesting.CNIPluginArgument)
expectedPluginArgument := servertesting.CNIPluginArgument{
NetnsPath: sandbox.NetNS,
Namespace: config.GetMetadata().GetNamespace(),
Name: config.GetMetadata().GetName(),
ContainerID: id,
}
assert.Equal(t, expectedPluginArgument, pluginArgument, "SetUpPod should be called with correct arguments")
}
func TestParseDNSOption(t *testing.T) {
for desc, test := range map[string]struct {
servers []string

View File

@ -1,218 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
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.
*/
package server
import (
"errors"
"testing"
"time"
"github.com/containerd/containerd/api/types/task"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/net/context"
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing"
sandboxstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/sandbox"
)
// Variables used in the following test.
const (
sandboxStatusTestID = "test-id"
sandboxStatusTestIP = "10.10.10.10"
sandboxStatusTestNetNS = "test-netns"
)
func getSandboxStatusTestData() (*sandboxstore.Sandbox, *runtime.PodSandboxStatus) {
config := &runtime.PodSandboxConfig{
Metadata: &runtime.PodSandboxMetadata{
Name: "test-name",
Uid: "test-uid",
Namespace: "test-ns",
Attempt: 1,
},
Linux: &runtime.LinuxPodSandboxConfig{
SecurityContext: &runtime.LinuxSandboxSecurityContext{
NamespaceOptions: &runtime.NamespaceOption{
HostNetwork: true,
HostPid: false,
HostIpc: true,
},
},
},
Labels: map[string]string{"a": "b"},
Annotations: map[string]string{"c": "d"},
}
createdAt := time.Now().UnixNano()
sandbox := &sandboxstore.Sandbox{
Metadata: sandboxstore.Metadata{
ID: sandboxStatusTestID,
Name: "test-name",
Config: config,
CreatedAt: createdAt,
NetNS: sandboxStatusTestNetNS,
},
}
expectedStatus := &runtime.PodSandboxStatus{
Id: sandboxStatusTestID,
Metadata: config.GetMetadata(),
CreatedAt: createdAt,
Network: &runtime.PodSandboxNetworkStatus{Ip: ""},
Linux: &runtime.LinuxPodSandboxStatus{
Namespaces: &runtime.Namespace{
Options: &runtime.NamespaceOption{
HostNetwork: true,
HostPid: false,
HostIpc: true,
},
},
},
Labels: config.GetLabels(),
Annotations: config.GetAnnotations(),
}
return sandbox, expectedStatus
}
func TestPodSandboxStatus(t *testing.T) {
for desc, test := range map[string]struct {
sandboxTasks []task.Task
injectSandbox bool
injectErr error
injectIP bool
injectCNIErr error
expectState runtime.PodSandboxState
expectErr bool
expectCalls []string
expectedCNICalls []string
}{
"sandbox status without metadata": {
injectSandbox: false,
expectErr: true,
expectCalls: []string{},
expectedCNICalls: []string{},
},
"sandbox status with running sandbox container": {
sandboxTasks: []task.Task{{
ID: sandboxStatusTestID,
Pid: 1,
Status: task.StatusRunning,
}},
injectSandbox: true,
expectState: runtime.PodSandboxState_SANDBOX_READY,
expectCalls: []string{"info"},
expectedCNICalls: []string{"GetContainerNetworkStatus"},
},
"sandbox status with stopped sandbox container": {
sandboxTasks: []task.Task{{
ID: sandboxStatusTestID,
Pid: 1,
Status: task.StatusStopped,
}},
injectSandbox: true,
expectState: runtime.PodSandboxState_SANDBOX_NOTREADY,
expectCalls: []string{"info"},
expectedCNICalls: []string{"GetContainerNetworkStatus"},
},
"sandbox status with non-existing sandbox container": {
sandboxTasks: []task.Task{},
injectSandbox: true,
expectState: runtime.PodSandboxState_SANDBOX_NOTREADY,
expectCalls: []string{"info"},
expectedCNICalls: []string{"GetContainerNetworkStatus"},
},
"sandbox status with arbitrary error": {
sandboxTasks: []task.Task{{
ID: sandboxStatusTestID,
Pid: 1,
Status: task.StatusRunning,
}},
injectSandbox: true,
expectState: runtime.PodSandboxState_SANDBOX_READY,
injectErr: errors.New("arbitrary error"),
expectErr: true,
expectCalls: []string{"info"},
expectedCNICalls: []string{},
},
"sandbox status with IP address": {
sandboxTasks: []task.Task{{
ID: sandboxStatusTestID,
Pid: 1,
Status: task.StatusRunning,
}},
injectSandbox: true,
expectState: runtime.PodSandboxState_SANDBOX_READY,
expectCalls: []string{"info"},
injectIP: true,
expectedCNICalls: []string{"GetContainerNetworkStatus"},
},
"sandbox status with GetContainerNetworkStatus returns error": {
sandboxTasks: []task.Task{{
ID: sandboxStatusTestID,
Pid: 1,
Status: task.StatusRunning,
}},
injectSandbox: true,
expectState: runtime.PodSandboxState_SANDBOX_READY,
expectCalls: []string{"info"},
expectedCNICalls: []string{"GetContainerNetworkStatus"},
injectCNIErr: errors.New("get container network status error"),
},
} {
t.Logf("TestCase %q", desc)
sandbox, expect := getSandboxStatusTestData()
expect.Network.Ip = ""
c := newTestCRIContainerdService()
fake := c.taskService.(*servertesting.FakeExecutionClient)
fakeCNIPlugin := c.netPlugin.(*servertesting.FakeCNIPlugin)
fake.SetFakeTasks(test.sandboxTasks)
if test.injectSandbox {
assert.NoError(t, c.sandboxStore.Add(*sandbox))
}
if test.injectErr != nil {
fake.InjectError("info", test.injectErr)
}
if test.injectCNIErr != nil {
fakeCNIPlugin.InjectError("GetContainerNetworkStatus", test.injectCNIErr)
}
if test.injectIP {
fakeCNIPlugin.SetFakePodNetwork(sandbox.NetNS, sandbox.Config.GetMetadata().GetNamespace(),
sandbox.Config.GetMetadata().GetName(), sandboxStatusTestID, sandboxStatusTestIP)
expect.Network.Ip = sandboxStatusTestIP
}
res, err := c.PodSandboxStatus(context.Background(), &runtime.PodSandboxStatusRequest{
PodSandboxId: sandboxStatusTestID,
})
assert.Equal(t, test.expectCalls, fake.GetCalledNames())
assert.Equal(t, test.expectedCNICalls, fakeCNIPlugin.GetCalledNames())
if test.expectErr {
assert.Error(t, err)
assert.Nil(t, res)
continue
}
assert.NoError(t, err)
require.NotNil(t, res)
expect.State = test.expectState
assert.Equal(t, expect, res.GetStatus())
}
}

View File

@ -1,292 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
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.
*/
package server
import (
"errors"
"os"
"testing"
"time"
"github.com/containerd/containerd/api/services/execution"
"github.com/containerd/containerd/api/types/task"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing"
servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing"
containerstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/container"
sandboxstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/sandbox"
)
func TestStopPodSandbox(t *testing.T) {
testID := "test-id"
testSandbox := sandboxstore.Sandbox{
Metadata: sandboxstore.Metadata{
ID: testID,
Name: "test-name",
Config: &runtime.PodSandboxConfig{
Metadata: &runtime.PodSandboxMetadata{
Name: "test-name",
Uid: "test-uid",
Namespace: "test-ns",
}},
NetNS: "test-netns",
},
}
testContainer := task.Task{
ID: testID,
Pid: 1,
Status: task.StatusRunning,
}
for desc, test := range map[string]struct {
sandboxTasks []task.Task
injectSandbox bool
injectErr error
injectStatErr error
injectCNIErr error
injectUnmountErr error
expectErr bool
expectCalls []string
expectedCNICalls []string
}{
"stop non-existing sandbox": {
injectSandbox: false,
expectErr: true,
expectCalls: []string{},
expectedCNICalls: []string{},
},
"stop sandbox with sandbox container": {
sandboxTasks: []task.Task{testContainer},
injectSandbox: true,
expectErr: false,
expectCalls: []string{"delete"},
expectedCNICalls: []string{"TearDownPod"},
},
"stop sandbox with sandbox container not exist error": {
sandboxTasks: []task.Task{},
injectSandbox: true,
// Inject error to make sure fake execution client returns error.
injectErr: servertesting.TaskNotExistError,
expectErr: false,
expectCalls: []string{"delete"},
expectedCNICalls: []string{"TearDownPod"},
},
"stop sandbox with with arbitrary error": {
injectSandbox: true,
injectErr: grpc.Errorf(codes.Unknown, "arbitrary error"),
expectErr: true,
expectCalls: []string{"delete"},
expectedCNICalls: []string{"TearDownPod"},
},
"stop sandbox with Stat returns arbitrary error": {
sandboxTasks: []task.Task{testContainer},
injectSandbox: true,
expectErr: true,
injectStatErr: errors.New("arbitrary error"),
expectCalls: []string{},
expectedCNICalls: []string{},
},
"stop sandbox with Stat returns not exist error": {
sandboxTasks: []task.Task{testContainer},
injectSandbox: true,
expectErr: false,
expectCalls: []string{"delete"},
injectStatErr: os.ErrNotExist,
expectedCNICalls: []string{},
},
"stop sandbox with TearDownPod fails": {
sandboxTasks: []task.Task{testContainer},
injectSandbox: true,
expectErr: true,
expectedCNICalls: []string{"TearDownPod"},
injectCNIErr: errors.New("arbitrary error"),
expectCalls: []string{},
},
"stop sandbox with unmount error": {
sandboxTasks: []task.Task{testContainer},
injectSandbox: true,
expectErr: true,
expectedCNICalls: []string{"TearDownPod"},
injectCNIErr: errors.New("arbitrary error"),
injectUnmountErr: errors.New("arbitrary error"),
expectCalls: []string{},
},
} {
t.Logf("TestCase %q", desc)
c := newTestCRIContainerdService()
fake := c.taskService.(*servertesting.FakeExecutionClient)
fakeCNIPlugin := c.netPlugin.(*servertesting.FakeCNIPlugin)
fakeOS := c.os.(*ostesting.FakeOS)
fake.SetFakeTasks(test.sandboxTasks)
if test.injectSandbox {
assert.NoError(t, c.sandboxStore.Add(testSandbox))
}
if test.injectErr != nil {
fake.InjectError("delete", test.injectErr)
}
if test.injectCNIErr != nil {
fakeCNIPlugin.InjectError("TearDownPod", test.injectCNIErr)
}
if test.injectStatErr != nil {
fakeOS.InjectError("Stat", test.injectStatErr)
}
if test.injectUnmountErr != nil {
fakeOS.InjectError("Unmount", test.injectUnmountErr)
}
fakeCNIPlugin.SetFakePodNetwork(testSandbox.NetNS, testSandbox.Config.GetMetadata().GetNamespace(),
testSandbox.Config.GetMetadata().GetName(), testID, sandboxStatusTestIP)
res, err := c.StopPodSandbox(context.Background(), &runtime.StopPodSandboxRequest{
PodSandboxId: testID,
})
if test.expectErr {
assert.Error(t, err)
assert.Nil(t, res)
} else {
assert.NoError(t, err)
assert.NotNil(t, res)
}
assert.Equal(t, test.expectCalls, fake.GetCalledNames())
assert.Equal(t, test.expectedCNICalls, fakeCNIPlugin.GetCalledNames())
}
}
func TestStopContainersInSandbox(t *testing.T) {
testID := "test-id"
testSandbox := sandboxstore.Sandbox{
Metadata: sandboxstore.Metadata{
ID: testID,
Name: "test-name",
Config: &runtime.PodSandboxConfig{
Metadata: &runtime.PodSandboxMetadata{
Name: "test-name",
Uid: "test-uid",
Namespace: "test-ns",
}},
NetNS: "test-netns",
},
}
testContainers := []containerForTest{
{
metadata: containerstore.Metadata{
ID: "test-cid-1",
Name: "test-cname-1",
SandboxID: testID,
},
status: containerstore.Status{
Pid: 2,
StartedAt: time.Now().UnixNano(),
},
},
{
metadata: containerstore.Metadata{
ID: "test-cid-2",
Name: "test-cname-2",
SandboxID: testID,
},
status: containerstore.Status{
Pid: 3,
StartedAt: time.Now().UnixNano(),
},
},
{
metadata: containerstore.Metadata{
ID: "test-cid-3",
Name: "test-cname-3",
SandboxID: "other-sandbox-id",
},
status: containerstore.Status{
Pid: 4,
StartedAt: time.Now().UnixNano(),
},
},
}
testContainerdContainers := []task.Task{
{
ID: testID,
Pid: 1,
Status: task.StatusRunning,
},
{
ID: "test-cid-1",
Pid: 2,
Status: task.StatusRunning,
},
{
ID: "test-cid-2",
Pid: 3,
Status: task.StatusRunning,
},
{
ID: "test-cid-3",
Pid: 4,
Status: task.StatusRunning,
},
}
c := newTestCRIContainerdService()
fake := servertesting.NewFakeExecutionClient().WithEvents()
defer fake.Stop()
c.taskService = fake
fake.SetFakeTasks(testContainerdContainers)
c.sandboxStore.Add(testSandbox)
for _, tc := range testContainers {
cntr, err := tc.toContainer()
assert.NoError(t, err)
assert.NoError(t, c.containerStore.Add(cntr))
}
fakeCNIPlugin := c.netPlugin.(*servertesting.FakeCNIPlugin)
fakeCNIPlugin.SetFakePodNetwork(testSandbox.NetNS, testSandbox.Config.GetMetadata().GetNamespace(),
testSandbox.Config.GetMetadata().GetName(), testID, sandboxStatusTestIP)
eventClient, err := fake.Events(context.Background(), &execution.EventsRequest{})
assert.NoError(t, err)
// Start a simple test event monitor.
go func(e execution.Tasks_EventsClient) {
for {
if err := c.handleEventStream(e); err != nil { // nolint: vetshadow
return
}
}
}(eventClient)
res, err := c.StopPodSandbox(context.Background(), &runtime.StopPodSandboxRequest{
PodSandboxId: testID,
})
assert.NoError(t, err)
assert.NotNil(t, res)
cntrs := c.containerStore.List()
assert.Len(t, cntrs, 3)
expectedStates := map[string]runtime.ContainerState{
"test-cid-1": runtime.ContainerState_CONTAINER_EXITED,
"test-cid-2": runtime.ContainerState_CONTAINER_EXITED,
"test-cid-3": runtime.ContainerState_CONTAINER_RUNNING,
}
for id, expected := range expectedStates {
cntr, err := c.containerStore.Get(id)
assert.NoError(t, err)
assert.Equal(t, expected, cntr.Status.Get().State())
}
}

View File

@ -18,16 +18,6 @@ package server
import (
"io"
"os"
"testing"
"github.com/containerd/containerd/api/services/execution"
snapshotservice "github.com/containerd/containerd/services/snapshot"
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/net/context"
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing"
"github.com/kubernetes-incubator/cri-containerd/pkg/registrar"
@ -64,132 +54,7 @@ func newTestCRIContainerdService() *criContainerdService {
sandboxNameIndex: registrar.NewRegistrar(),
containerStore: containerstore.NewStore(),
containerNameIndex: registrar.NewRegistrar(),
taskService: servertesting.NewFakeExecutionClient(),
containerService: servertesting.NewFakeContainersClient(),
netPlugin: servertesting.NewFakeCNIPlugin(),
agentFactory: agentstesting.NewFakeAgentFactory(),
}
}
// WithFakeSnapshotClient add and return fake snapshot client.
func WithFakeSnapshotClient(c *criContainerdService) *servertesting.FakeSnapshotClient {
fake := servertesting.NewFakeSnapshotClient()
c.snapshotService = snapshotservice.NewSnapshotterFromClient(fake)
return fake
}
// Test all sandbox operations.
func TestSandboxOperations(t *testing.T) {
c := newTestCRIContainerdService()
fake := c.taskService.(*servertesting.FakeExecutionClient)
fakeOS := c.os.(*ostesting.FakeOS)
fakeCNIPlugin := c.netPlugin.(*servertesting.FakeCNIPlugin)
WithFakeSnapshotClient(c)
fakeOS.OpenFifoFn = func(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
return nopReadWriteCloser{}, nil
}
// Insert sandbox image metadata.
c.imageStore.Add(imagestore.Image{
ID: testSandboxImage,
ChainID: "test-chain-id",
Config: &imagespec.ImageConfig{Entrypoint: []string{"/pause"}},
})
config := &runtime.PodSandboxConfig{
Metadata: &runtime.PodSandboxMetadata{
Name: "test-name",
Uid: "test-uid",
Namespace: "test-ns",
Attempt: 1,
},
Hostname: "test-hostname",
LogDirectory: "test-log-directory",
Labels: map[string]string{"a": "b"},
Annotations: map[string]string{"c": "d"},
}
t.Logf("should be able to run a pod sandbox")
runRes, err := c.RunPodSandbox(context.Background(), &runtime.RunPodSandboxRequest{Config: config})
assert.NoError(t, err)
require.NotNil(t, runRes)
id := runRes.GetPodSandboxId()
t.Logf("should be able to get pod sandbox status")
info, err := fake.Info(context.Background(), &execution.InfoRequest{ContainerID: id})
netns := getNetworkNamespace(info.Task.Pid)
assert.NoError(t, err)
expectSandboxStatus := &runtime.PodSandboxStatus{
Id: id,
Metadata: config.GetMetadata(),
// TODO(random-liu): [P2] Use fake clock for CreatedAt.
Network: &runtime.PodSandboxNetworkStatus{},
Linux: &runtime.LinuxPodSandboxStatus{
Namespaces: &runtime.Namespace{
Options: &runtime.NamespaceOption{
HostNetwork: false,
HostPid: false,
HostIpc: false,
},
},
},
Labels: config.GetLabels(),
Annotations: config.GetAnnotations(),
}
statusRes, err := c.PodSandboxStatus(context.Background(), &runtime.PodSandboxStatusRequest{PodSandboxId: id})
assert.NoError(t, err)
require.NotNil(t, statusRes)
status := statusRes.GetStatus()
expectSandboxStatus.CreatedAt = status.GetCreatedAt()
ip, err := fakeCNIPlugin.GetContainerNetworkStatus(netns, config.GetMetadata().GetNamespace(), config.GetMetadata().GetName(), id)
assert.NoError(t, err)
expectSandboxStatus.Network.Ip = ip
assert.Equal(t, expectSandboxStatus, status)
t.Logf("should be able to list pod sandboxes")
expectSandbox := &runtime.PodSandbox{
Id: id,
Metadata: config.GetMetadata(),
State: runtime.PodSandboxState_SANDBOX_NOTREADY, // TODO(mikebrow) converting to client... should this be ready?
Labels: config.GetLabels(),
Annotations: config.GetAnnotations(),
}
listRes, err := c.ListPodSandbox(context.Background(), &runtime.ListPodSandboxRequest{})
assert.NoError(t, err)
require.NotNil(t, listRes)
sandboxes := listRes.GetItems()
assert.Len(t, sandboxes, 1)
expectSandbox.CreatedAt = sandboxes[0].CreatedAt
assert.Equal(t, expectSandbox, sandboxes[0])
t.Logf("should be able to stop a pod sandbox")
stopRes, err := c.StopPodSandbox(context.Background(), &runtime.StopPodSandboxRequest{PodSandboxId: id})
assert.NoError(t, err)
require.NotNil(t, stopRes)
statusRes, err = c.PodSandboxStatus(context.Background(), &runtime.PodSandboxStatusRequest{PodSandboxId: id})
assert.NoError(t, err)
require.NotNil(t, statusRes)
assert.Equal(t, runtime.PodSandboxState_SANDBOX_NOTREADY, statusRes.GetStatus().GetState(),
"sandbox status should be NOTREADY after stopped")
listRes, err = c.ListPodSandbox(context.Background(), &runtime.ListPodSandboxRequest{})
assert.NoError(t, err)
require.NotNil(t, listRes)
assert.Len(t, listRes.GetItems(), 1)
assert.Equal(t, runtime.PodSandboxState_SANDBOX_NOTREADY, listRes.GetItems()[0].State,
"sandbox in list should be NOTREADY after stopped")
t.Logf("should be able to remove a pod sandbox")
removeRes, err := c.RemovePodSandbox(context.Background(), &runtime.RemovePodSandboxRequest{PodSandboxId: id})
assert.NoError(t, err)
require.NotNil(t, removeRes)
_, err = c.PodSandboxStatus(context.Background(), &runtime.PodSandboxStatusRequest{PodSandboxId: id})
assert.Error(t, err, "should not be able to get sandbox status after removed")
listRes, err = c.ListPodSandbox(context.Background(), &runtime.ListPodSandboxRequest{})
assert.NoError(t, err)
require.NotNil(t, listRes)
assert.Empty(t, listRes.GetItems(), "should not be able to list the sandbox after removed")
t.Logf("should be able to create the sandbox again")
runRes, err = c.RunPodSandbox(context.Background(), &runtime.RunPodSandboxRequest{Config: config})
assert.NoError(t, err)
require.NotNil(t, runRes)
}

View File

@ -26,6 +26,14 @@ import (
"github.com/kubernetes-incubator/cri-o/pkg/ocicni"
)
// CalledDetail is the struct contains called function name and arguments.
type CalledDetail struct {
// Name of the function called.
Name string
// Argument of the function called.
Argument interface{}
}
// CNIPluginArgument is arguments used to call CNI related functions.
type CNIPluginArgument struct {
NetnsPath string

View File

@ -1,181 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
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.
*/
package testing
import (
"fmt"
"sync"
"github.com/containerd/containerd/api/services/containers"
googleprotobuf "github.com/golang/protobuf/ptypes/empty"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
)
// ContainerNotExistError is the fake error returned when container does not exist.
var ContainerNotExistError = grpc.Errorf(codes.NotFound, "container does not exist")
// FakeContainersClient is a simple fake containers client, so that cri-containerd
// can be run for testing without requiring a real containerd setup.
type FakeContainersClient struct {
sync.Mutex
called []CalledDetail
errors map[string]error
ContainerList map[string]containers.Container
}
var _ containers.ContainersClient = &FakeContainersClient{}
// NewFakeContainersClient creates a FakeContainersClient
func NewFakeContainersClient() *FakeContainersClient {
return &FakeContainersClient{
errors: make(map[string]error),
ContainerList: make(map[string]containers.Container),
}
}
func (f *FakeContainersClient) getError(op string) error {
err, ok := f.errors[op]
if ok {
delete(f.errors, op)
return err
}
return nil
}
// InjectError inject error for call
func (f *FakeContainersClient) InjectError(fn string, err error) {
f.Lock()
defer f.Unlock()
f.errors[fn] = err
}
// InjectErrors inject errors for calls
func (f *FakeContainersClient) InjectErrors(errs map[string]error) {
f.Lock()
defer f.Unlock()
for fn, err := range errs {
f.errors[fn] = err
}
}
// ClearErrors clear errors for call
func (f *FakeContainersClient) ClearErrors() {
f.Lock()
defer f.Unlock()
f.errors = make(map[string]error)
}
func (f *FakeContainersClient) appendCalled(name string, argument interface{}) {
call := CalledDetail{Name: name, Argument: argument}
f.called = append(f.called, call)
}
// GetCalledNames get names of call
func (f *FakeContainersClient) GetCalledNames() []string {
f.Lock()
defer f.Unlock()
names := []string{}
for _, detail := range f.called {
names = append(names, detail.Name)
}
return names
}
// ClearCalls clear all call detail.
func (f *FakeContainersClient) ClearCalls() {
f.Lock()
defer f.Unlock()
f.called = []CalledDetail{}
}
// GetCalledDetails get detail of each call.
func (f *FakeContainersClient) GetCalledDetails() []CalledDetail {
f.Lock()
defer f.Unlock()
// Copy the list and return.
return append([]CalledDetail{}, f.called...)
}
// Create is a test implementation of containers.Create.
func (f *FakeContainersClient) Create(ctx context.Context, createOpts *containers.CreateContainerRequest, opts ...grpc.CallOption) (*containers.CreateContainerResponse, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("create", createOpts)
if err := f.getError("create"); err != nil {
return nil, err
}
_, ok := f.ContainerList[createOpts.Container.ID]
if ok {
return nil, fmt.Errorf("container already exists")
}
f.ContainerList[createOpts.Container.ID] = createOpts.Container
return &containers.CreateContainerResponse{Container: createOpts.Container}, nil
}
// Delete is a test implementation of containers.Delete
func (f *FakeContainersClient) Delete(ctx context.Context, deleteOpts *containers.DeleteContainerRequest, opts ...grpc.CallOption) (*googleprotobuf.Empty, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("delete", deleteOpts)
if err := f.getError("delete"); err != nil {
return nil, err
}
_, ok := f.ContainerList[deleteOpts.ID]
if !ok {
return nil, ContainerNotExistError
}
delete(f.ContainerList, deleteOpts.ID)
return &googleprotobuf.Empty{}, nil
}
// Get is a test implementation of containers.Get
func (f *FakeContainersClient) Get(ctx context.Context, getOpts *containers.GetContainerRequest, opts ...grpc.CallOption) (*containers.GetContainerResponse, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("get", getOpts)
if err := f.getError("get"); err != nil {
return nil, err
}
c, ok := f.ContainerList[getOpts.ID]
if !ok {
return nil, ContainerNotExistError
}
return &containers.GetContainerResponse{Container: c}, nil
}
// List is a test implementation of containers.List
func (f *FakeContainersClient) List(ctx context.Context, listOpts *containers.ListContainersRequest, opts ...grpc.CallOption) (*containers.ListContainersResponse, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("list", listOpts)
if err := f.getError("list"); err != nil {
return nil, err
}
var cs []containers.Container
for _, c := range f.ContainerList {
cs = append(cs, c)
}
return &containers.ListContainersResponse{Containers: cs}, nil
}
// Update is a test implementation of containers.Update
func (f *FakeContainersClient) Update(ctx context.Context, updateOpts *containers.UpdateContainerRequest, opts ...grpc.CallOption) (*containers.UpdateContainerResponse, error) {
// TODO: implement Update()
return nil, nil
}

View File

@ -1,392 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
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.
*/
package testing
import (
"fmt"
"math/rand"
"sync"
"time"
"github.com/containerd/containerd/api/services/execution"
"github.com/containerd/containerd/api/types/task"
googleprotobuf "github.com/golang/protobuf/ptypes/empty"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
)
// TaskNotExistError is the fake error returned when task does not exist.
var TaskNotExistError = grpc.Errorf(codes.NotFound, "task does not exist")
// CalledDetail is the struct contains called function name and arguments.
type CalledDetail struct {
// Name of the function called.
Name string
// Argument of the function called.
Argument interface{}
}
var _ execution.Tasks_EventsClient = &EventClient{}
// EventClient is a test implementation of execution.Tasks_EventsClient
type EventClient struct {
Events chan *task.Event
grpc.ClientStream
}
// Recv is a test implementation of Recv
func (cli *EventClient) Recv() (*task.Event, error) {
event, ok := <-cli.Events
if !ok {
return nil, fmt.Errorf("event channel closed")
}
return event, nil
}
// FakeExecutionClient is a simple fake execution client, so that cri-containerd
// can be run for testing without requiring a real containerd setup.
type FakeExecutionClient struct {
sync.Mutex
called []CalledDetail
errors map[string]error
TaskList map[string]task.Task
eventsQueue chan *task.Event
eventClients []*EventClient
}
var _ execution.TasksClient = &FakeExecutionClient{}
// NewFakeExecutionClient creates a FakeExecutionClient
func NewFakeExecutionClient() *FakeExecutionClient {
return &FakeExecutionClient{
errors: make(map[string]error),
TaskList: make(map[string]task.Task),
}
}
// Stop the fake execution service. Needed when event is enabled.
func (f *FakeExecutionClient) Stop() {
if f.eventsQueue != nil {
close(f.eventsQueue)
}
f.Lock()
defer f.Unlock()
for _, client := range f.eventClients {
close(client.Events)
}
f.eventClients = nil
}
// WithEvents setup events publisher for FakeExecutionClient
func (f *FakeExecutionClient) WithEvents() *FakeExecutionClient {
f.eventsQueue = make(chan *task.Event, 1024)
go func() {
for e := range f.eventsQueue {
f.Lock()
for _, client := range f.eventClients {
client.Events <- e
}
f.Unlock()
}
}()
return f
}
func (f *FakeExecutionClient) getError(op string) error {
err, ok := f.errors[op]
if ok {
delete(f.errors, op)
return err
}
return nil
}
// InjectError inject error for call
func (f *FakeExecutionClient) InjectError(fn string, err error) {
f.Lock()
defer f.Unlock()
f.errors[fn] = err
}
// InjectErrors inject errors for calls
func (f *FakeExecutionClient) InjectErrors(errs map[string]error) {
f.Lock()
defer f.Unlock()
for fn, err := range errs {
f.errors[fn] = err
}
}
// ClearErrors clear errors for call
func (f *FakeExecutionClient) ClearErrors() {
f.Lock()
defer f.Unlock()
f.errors = make(map[string]error)
}
func generatePid() uint32 {
rand.Seed(time.Now().Unix())
randPid := uint32(rand.Intn(1000))
return randPid
}
func (f *FakeExecutionClient) sendEvent(event *task.Event) {
if f.eventsQueue != nil {
f.eventsQueue <- event
}
}
func (f *FakeExecutionClient) appendCalled(name string, argument interface{}) {
call := CalledDetail{Name: name, Argument: argument}
f.called = append(f.called, call)
}
// GetCalledNames get names of call
func (f *FakeExecutionClient) GetCalledNames() []string {
f.Lock()
defer f.Unlock()
names := []string{}
for _, detail := range f.called {
names = append(names, detail.Name)
}
return names
}
// ClearCalls clear all call detail.
func (f *FakeExecutionClient) ClearCalls() {
f.Lock()
defer f.Unlock()
f.called = []CalledDetail{}
}
// GetCalledDetails get detail of each call.
func (f *FakeExecutionClient) GetCalledDetails() []CalledDetail {
f.Lock()
defer f.Unlock()
// Copy the list and return.
return append([]CalledDetail{}, f.called...)
}
// SetFakeTasks injects fake tasks.
func (f *FakeExecutionClient) SetFakeTasks(tasks []task.Task) {
f.Lock()
defer f.Unlock()
for _, t := range tasks {
f.TaskList[t.ID] = t
}
}
// Create is a test implementation of execution.Create.
func (f *FakeExecutionClient) Create(ctx context.Context, createOpts *execution.CreateRequest, opts ...grpc.CallOption) (*execution.CreateResponse, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("create", createOpts)
if err := f.getError("create"); err != nil {
return nil, err
}
_, ok := f.TaskList[createOpts.ContainerID]
if ok {
return nil, fmt.Errorf("task already exists")
}
pid := generatePid()
f.TaskList[createOpts.ContainerID] = task.Task{
ContainerID: createOpts.ContainerID,
Pid: pid,
Status: task.StatusCreated,
}
f.sendEvent(&task.Event{
ID: createOpts.ContainerID,
Type: task.Event_CREATE,
Pid: pid,
})
return &execution.CreateResponse{
ContainerID: createOpts.ContainerID,
Pid: pid,
}, nil
}
// Start is a test implementation of execution.Start
func (f *FakeExecutionClient) Start(ctx context.Context, startOpts *execution.StartRequest, opts ...grpc.CallOption) (*googleprotobuf.Empty, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("start", startOpts)
if err := f.getError("start"); err != nil {
return nil, err
}
c, ok := f.TaskList[startOpts.ContainerID]
if !ok {
return nil, TaskNotExistError
}
f.sendEvent(&task.Event{
ID: c.ID,
Type: task.Event_START,
Pid: c.Pid,
})
switch c.Status {
case task.StatusCreated:
c.Status = task.StatusRunning
f.TaskList[startOpts.ContainerID] = c
return &googleprotobuf.Empty{}, nil
case task.StatusStopped:
return &googleprotobuf.Empty{}, fmt.Errorf("cannot start a container that has stopped")
case task.StatusRunning:
return &googleprotobuf.Empty{}, fmt.Errorf("cannot start an already running container")
default:
return &googleprotobuf.Empty{}, fmt.Errorf("cannot start a container in the %s state", c.Status)
}
}
// Delete is a test implementation of execution.Delete
func (f *FakeExecutionClient) Delete(ctx context.Context, deleteOpts *execution.DeleteRequest, opts ...grpc.CallOption) (*execution.DeleteResponse, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("delete", deleteOpts)
if err := f.getError("delete"); err != nil {
return nil, err
}
c, ok := f.TaskList[deleteOpts.ContainerID]
if !ok {
return nil, TaskNotExistError
}
delete(f.TaskList, deleteOpts.ContainerID)
f.sendEvent(&task.Event{
ID: c.ID,
Type: task.Event_EXIT,
Pid: c.Pid,
})
return nil, nil
}
// Info is a test implementation of execution.Info
func (f *FakeExecutionClient) Info(ctx context.Context, infoOpts *execution.InfoRequest, opts ...grpc.CallOption) (*execution.InfoResponse, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("info", infoOpts)
if err := f.getError("info"); err != nil {
return nil, err
}
c, ok := f.TaskList[infoOpts.ContainerID]
if !ok {
return nil, TaskNotExistError
}
return &execution.InfoResponse{Task: &c}, nil
}
// List is a test implementation of execution.List
func (f *FakeExecutionClient) List(ctx context.Context, listOpts *execution.ListRequest, opts ...grpc.CallOption) (*execution.ListResponse, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("list", listOpts)
if err := f.getError("list"); err != nil {
return nil, err
}
resp := &execution.ListResponse{}
for _, c := range f.TaskList {
resp.Tasks = append(resp.Tasks, &task.Task{
ID: c.ID,
Pid: c.Pid,
Status: c.Status,
})
}
return resp, nil
}
// Kill is a test implementation of execution.Kill
func (f *FakeExecutionClient) Kill(ctx context.Context, killOpts *execution.KillRequest, opts ...grpc.CallOption) (*googleprotobuf.Empty, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("kill", killOpts)
if err := f.getError("kill"); err != nil {
return nil, err
}
c, ok := f.TaskList[killOpts.ContainerID]
if !ok {
return nil, TaskNotExistError
}
c.Status = task.StatusStopped
f.TaskList[killOpts.ContainerID] = c
f.sendEvent(&task.Event{
ID: c.ID,
Type: task.Event_EXIT,
Pid: c.Pid,
})
return &googleprotobuf.Empty{}, nil
}
// Events is a test implementation of execution.Events
func (f *FakeExecutionClient) Events(ctx context.Context, eventsOpts *execution.EventsRequest, opts ...grpc.CallOption) (execution.Tasks_EventsClient, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("events", eventsOpts)
if err := f.getError("events"); err != nil {
return nil, err
}
var client = &EventClient{
Events: make(chan *task.Event, 100),
}
f.eventClients = append(f.eventClients, client)
return client, nil
}
// Exec is a test implementation of execution.Exec
func (f *FakeExecutionClient) Exec(ctx context.Context, execOpts *execution.ExecRequest, opts ...grpc.CallOption) (*execution.ExecResponse, error) {
// TODO: implement Exec()
return nil, nil
}
// Pty is a test implementation of execution.Pty
func (f *FakeExecutionClient) Pty(ctx context.Context, ptyOpts *execution.PtyRequest, opts ...grpc.CallOption) (*googleprotobuf.Empty, error) {
// TODO: implement Pty()
return nil, nil
}
// CloseStdin is a test implementation of execution.CloseStdin
func (f *FakeExecutionClient) CloseStdin(ctx context.Context, closeStdinOpts *execution.CloseStdinRequest, opts ...grpc.CallOption) (*googleprotobuf.Empty, error) {
// TODO: implement CloseStdin()
return nil, nil
}
// Pause is a test implementation of execution.Pause
func (f *FakeExecutionClient) Pause(ctx context.Context, in *execution.PauseRequest, opts ...grpc.CallOption) (*googleprotobuf.Empty, error) {
// TODO: implement Pause()
return nil, nil
}
// Resume is a test implementation of execution.Resume
func (f *FakeExecutionClient) Resume(ctx context.Context, in *execution.ResumeRequest, opts ...grpc.CallOption) (*googleprotobuf.Empty, error) {
// TODO: implement Resume()
return nil, nil
}
// Checkpoint is a test implementation of execution.Checkpoint
func (f *FakeExecutionClient) Checkpoint(ctx context.Context, in *execution.CheckpointRequest, opts ...grpc.CallOption) (*execution.CheckpointResponse, error) {
// TODO: implement Checkpoint()
return nil, nil
}
// Processes is a test implementation of execution.Processes
func (f *FakeExecutionClient) Processes(ctx context.Context, in *execution.ProcessesRequest, opts ...grpc.CallOption) (*execution.ProcessesResponse, error) {
// TODO: implement Processes()
return nil, nil
}
// DeleteProcess is a test implementation of execution.DeleteProcess
func (f *FakeExecutionClient) DeleteProcess(ctx context.Context, in *execution.DeleteProcessRequest, opts ...grpc.CallOption) (*execution.DeleteResponse, error) {
// TODO: implement DeleteProcess()
return nil, nil
}

View File

@ -1,164 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
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.
*/
package testing
import (
"fmt"
"sync"
"github.com/containerd/containerd/api/services/images"
googleprotobuf "github.com/golang/protobuf/ptypes/empty"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
// FakeImagesClient is a simple fake images client, so that cri-containerd
// can be run for testing without requiring a real containerd setup.
type FakeImagesClient struct {
sync.Mutex
called []CalledDetail
errors map[string]error
ImageList map[string]images.Image
}
var _ images.ImagesClient = &FakeImagesClient{}
// NewFakeImagesClient creates a FakeImagesClient
func NewFakeImagesClient() *FakeImagesClient {
return &FakeImagesClient{
errors: make(map[string]error),
ImageList: make(map[string]images.Image),
}
}
// getError get error for call
func (f *FakeImagesClient) getError(op string) error {
err, ok := f.errors[op]
if ok {
delete(f.errors, op)
return err
}
return nil
}
// InjectError inject error for call
func (f *FakeImagesClient) InjectError(fn string, err error) {
f.Lock()
defer f.Unlock()
f.errors[fn] = err
}
// InjectErrors inject errors for calls
func (f *FakeImagesClient) InjectErrors(errs map[string]error) {
f.Lock()
defer f.Unlock()
for fn, err := range errs {
f.errors[fn] = err
}
}
// ClearErrors clear errors for call
func (f *FakeImagesClient) ClearErrors() {
f.Lock()
defer f.Unlock()
f.errors = make(map[string]error)
}
func (f *FakeImagesClient) appendCalled(name string, argument interface{}) {
call := CalledDetail{Name: name, Argument: argument}
f.called = append(f.called, call)
}
// GetCalledNames get names of call
func (f *FakeImagesClient) GetCalledNames() []string {
f.Lock()
defer f.Unlock()
names := []string{}
for _, detail := range f.called {
names = append(names, detail.Name)
}
return names
}
// SetFakeImages injects fake images.
func (f *FakeImagesClient) SetFakeImages(images []images.Image) {
f.Lock()
defer f.Unlock()
for _, image := range images {
f.ImageList[image.Name] = image
}
}
// Get is a test implementation of images.Get
func (f *FakeImagesClient) Get(ctx context.Context, getOpts *images.GetRequest, opts ...grpc.CallOption) (*images.GetResponse, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("get", getOpts)
if err := f.getError("get"); err != nil {
return nil, err
}
image, ok := f.ImageList[getOpts.Name]
if !ok {
return nil, fmt.Errorf("image does not exist")
}
return &images.GetResponse{
Image: &image,
}, nil
}
// Put is a test implementation of images.Put
func (f *FakeImagesClient) Put(ctx context.Context, putOpts *images.PutRequest, opts ...grpc.CallOption) (*googleprotobuf.Empty, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("put", putOpts)
if err := f.getError("put"); err != nil {
return nil, err
}
f.ImageList[putOpts.Image.Name] = putOpts.Image
return &googleprotobuf.Empty{}, nil
}
// List is a test implementation of images.List
func (f *FakeImagesClient) List(ctx context.Context, listOpts *images.ListRequest, opts ...grpc.CallOption) (*images.ListResponse, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("list", listOpts)
if err := f.getError("list"); err != nil {
return nil, err
}
resp := &images.ListResponse{}
for _, image := range f.ImageList {
resp.Images = append(resp.Images, image)
}
return resp, nil
}
// Delete is a test implementation of images.Delete
func (f *FakeImagesClient) Delete(ctx context.Context, deleteOpts *images.DeleteRequest, opts ...grpc.CallOption) (*googleprotobuf.Empty, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("delete", deleteOpts)
if err := f.getError("delete"); err != nil {
return nil, err
}
_, ok := f.ImageList[deleteOpts.Name]
if !ok {
return nil, fmt.Errorf("image does not exist")
}
delete(f.ImageList, deleteOpts.Name)
return &googleprotobuf.Empty{}, nil
}

View File

@ -1,221 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
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.
*/
package testing
import (
"fmt"
"sync"
"github.com/containerd/containerd/api/services/snapshot"
"github.com/containerd/containerd/api/types/mount"
google_protobuf1 "github.com/golang/protobuf/ptypes/empty"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
)
// SnapshotNotExistError is the fake error returned when snapshot does not exist.
var SnapshotNotExistError = grpc.Errorf(codes.NotFound, "snapshot does not exist")
// FakeSnapshotClient is a simple fake snapshot client, so that cri-containerd
// can be run for testing without requiring a real containerd setup.
type FakeSnapshotClient struct {
sync.Mutex
called []CalledDetail
errors map[string]error
MountList map[string][]*mount.Mount
}
var _ snapshot.SnapshotClient = &FakeSnapshotClient{}
// NewFakeSnapshotClient creates a FakeSnapshotClient
func NewFakeSnapshotClient() *FakeSnapshotClient {
return &FakeSnapshotClient{
errors: make(map[string]error),
MountList: make(map[string][]*mount.Mount),
}
}
func (f *FakeSnapshotClient) getError(op string) error {
err, ok := f.errors[op]
if ok {
delete(f.errors, op)
return err
}
return nil
}
// InjectError inject error for call
func (f *FakeSnapshotClient) InjectError(fn string, err error) {
f.Lock()
defer f.Unlock()
f.errors[fn] = err
}
// InjectErrors inject errors for calls
func (f *FakeSnapshotClient) InjectErrors(errs map[string]error) {
f.Lock()
defer f.Unlock()
for fn, err := range errs {
f.errors[fn] = err
}
}
// ClearErrors clear errors for call
func (f *FakeSnapshotClient) ClearErrors() {
f.Lock()
defer f.Unlock()
f.errors = make(map[string]error)
}
func (f *FakeSnapshotClient) appendCalled(name string, argument interface{}) {
call := CalledDetail{Name: name, Argument: argument}
f.called = append(f.called, call)
}
// GetCalledNames get names of call
func (f *FakeSnapshotClient) GetCalledNames() []string {
f.Lock()
defer f.Unlock()
names := []string{}
for _, detail := range f.called {
names = append(names, detail.Name)
}
return names
}
// GetCalledDetails get detail of each call.
func (f *FakeSnapshotClient) GetCalledDetails() []CalledDetail {
f.Lock()
defer f.Unlock()
// Copy the list and return.
return append([]CalledDetail{}, f.called...)
}
// SetFakeMounts injects fake mounts.
func (f *FakeSnapshotClient) SetFakeMounts(name string, mounts []*mount.Mount) {
f.Lock()
defer f.Unlock()
f.MountList[name] = mounts
}
// ListMounts lists all the fake mounts.
func (f *FakeSnapshotClient) ListMounts() [][]*mount.Mount {
f.Lock()
defer f.Unlock()
var ms [][]*mount.Mount
for _, m := range f.MountList {
ms = append(ms, m)
}
return ms
}
// Prepare is a test implementation of snapshot.Prepare
func (f *FakeSnapshotClient) Prepare(ctx context.Context, prepareOpts *snapshot.PrepareRequest, opts ...grpc.CallOption) (*snapshot.MountsResponse, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("prepare", prepareOpts)
if err := f.getError("prepare"); err != nil {
return nil, err
}
_, ok := f.MountList[prepareOpts.Key]
if ok {
return nil, fmt.Errorf("mounts already exist")
}
f.MountList[prepareOpts.Key] = []*mount.Mount{{
Type: "bind",
Source: prepareOpts.Key,
// TODO(random-liu): Fake options based on Readonly option.
}}
return &snapshot.MountsResponse{
Mounts: f.MountList[prepareOpts.Key],
}, nil
}
// Mounts is a test implementation of snapshot.Mounts
func (f *FakeSnapshotClient) Mounts(ctx context.Context, mountsOpts *snapshot.MountsRequest, opts ...grpc.CallOption) (*snapshot.MountsResponse, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("mounts", mountsOpts)
if err := f.getError("mounts"); err != nil {
return nil, err
}
mounts, ok := f.MountList[mountsOpts.Key]
if !ok {
return nil, SnapshotNotExistError
}
return &snapshot.MountsResponse{
Mounts: mounts,
}, nil
}
// Commit is a test implementation of snapshot.Commit
func (f *FakeSnapshotClient) Commit(ctx context.Context, in *snapshot.CommitRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) {
return nil, nil
}
// View is a test implementation of snapshot.View
func (f *FakeSnapshotClient) View(ctx context.Context, viewOpts *snapshot.PrepareRequest, opts ...grpc.CallOption) (*snapshot.MountsResponse, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("view", viewOpts)
if err := f.getError("view"); err != nil {
return nil, err
}
_, ok := f.MountList[viewOpts.Key]
if ok {
return nil, fmt.Errorf("mounts already exist")
}
f.MountList[viewOpts.Key] = []*mount.Mount{{
Type: "bind",
Source: viewOpts.Key,
// TODO(random-liu): Fake options based on Readonly option.
}}
return &snapshot.MountsResponse{
Mounts: f.MountList[viewOpts.Key],
}, nil
}
// Remove is a test implementation of snapshot.Remove
func (f *FakeSnapshotClient) Remove(ctx context.Context, removeOpts *snapshot.RemoveRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("remove", removeOpts)
if err := f.getError("remove"); err != nil {
return nil, err
}
if _, ok := f.MountList[removeOpts.Key]; !ok {
return nil, SnapshotNotExistError
}
delete(f.MountList, removeOpts.Key)
return &google_protobuf1.Empty{}, nil
}
// Stat is a test implementation of snapshot.Stat
func (f *FakeSnapshotClient) Stat(ctx context.Context, in *snapshot.StatRequest, opts ...grpc.CallOption) (*snapshot.StatResponse, error) {
return nil, nil
}
// List is a test implementation of snapshot.List
func (f *FakeSnapshotClient) List(ctx context.Context, in *snapshot.ListRequest, opts ...grpc.CallOption) (snapshot.Snapshot_ListClient, error) {
return nil, nil
}
// Usage is a test implementation of snapshot.Usage
func (f *FakeSnapshotClient) Usage(ctx context.Context, in *snapshot.UsageRequest, opts ...grpc.CallOption) (*snapshot.UsageResponse, error) {
return nil, nil
}