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
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"testing"
|
"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"
|
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/opencontainers/runtime-tools/generate"
|
"github.com/opencontainers/runtime-tools/generate"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/net/context"
|
|
||||||
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
"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,
|
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
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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"
|
"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"
|
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
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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"
|
"github.com/stretchr/testify/assert"
|
||||||
"golang.org/x/net/context"
|
"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"
|
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) {
|
func TestWaitContainerStop(t *testing.T) {
|
||||||
@ -93,240 +85,3 @@ func TestWaitContainerStop(t *testing.T) {
|
|||||||
assert.Equal(t, test.expectErr, err != nil, desc)
|
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"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"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"
|
"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"
|
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)
|
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
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
|
||||||
"testing"
|
"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"
|
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/net/context"
|
|
||||||
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||||
|
|
||||||
ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing"
|
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)) {
|
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) {
|
func TestParseDNSOption(t *testing.T) {
|
||||||
for desc, test := range map[string]struct {
|
for desc, test := range map[string]struct {
|
||||||
servers []string
|
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 (
|
import (
|
||||||
"io"
|
"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"
|
ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing"
|
||||||
"github.com/kubernetes-incubator/cri-containerd/pkg/registrar"
|
"github.com/kubernetes-incubator/cri-containerd/pkg/registrar"
|
||||||
@ -64,132 +54,7 @@ func newTestCRIContainerdService() *criContainerdService {
|
|||||||
sandboxNameIndex: registrar.NewRegistrar(),
|
sandboxNameIndex: registrar.NewRegistrar(),
|
||||||
containerStore: containerstore.NewStore(),
|
containerStore: containerstore.NewStore(),
|
||||||
containerNameIndex: registrar.NewRegistrar(),
|
containerNameIndex: registrar.NewRegistrar(),
|
||||||
taskService: servertesting.NewFakeExecutionClient(),
|
|
||||||
containerService: servertesting.NewFakeContainersClient(),
|
|
||||||
netPlugin: servertesting.NewFakeCNIPlugin(),
|
netPlugin: servertesting.NewFakeCNIPlugin(),
|
||||||
agentFactory: agentstesting.NewFakeAgentFactory(),
|
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"
|
"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.
|
// CNIPluginArgument is arguments used to call CNI related functions.
|
||||||
type CNIPluginArgument struct {
|
type CNIPluginArgument struct {
|
||||||
NetnsPath string
|
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