Temporarily remove unit test relying on fake containerd services.
Signed-off-by: Lantao Liu <lantaol@google.com>
This commit is contained in:
parent
cbd936b734
commit
ffb69423ec
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
@ -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
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
@ -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())
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue
Block a user