Temporarily remove unit test relying on fake containerd services.
Signed-off-by: Lantao Liu <lantaol@google.com>
This commit is contained in:
		| @@ -17,26 +17,14 @@ limitations under the License. | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"os" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/containerd/containerd/api/services/containers" | ||||
| 	snapshotapi "github.com/containerd/containerd/api/services/snapshot" | ||||
| 	imagespec "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| 	runtimespec "github.com/opencontainers/runtime-spec/specs-go" | ||||
| 	"github.com/opencontainers/runtime-tools/generate" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" | ||||
|  | ||||
| 	ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing" | ||||
| 	servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing" | ||||
| 	containerstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/container" | ||||
| 	imagestore "github.com/kubernetes-incubator/cri-containerd/pkg/store/image" | ||||
| 	sandboxstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/sandbox" | ||||
| ) | ||||
|  | ||||
| func checkMount(t *testing.T, mounts []runtimespec.Mount, src, dest, typ string, | ||||
| @@ -440,159 +428,3 @@ func TestPrivilegedBindMount(t *testing.T) { | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestCreateContainer(t *testing.T) { | ||||
| 	testSandboxID := "test-sandbox-id" | ||||
| 	testSandboxPid := uint32(4321) | ||||
| 	config, sandboxConfig, imageConfig, specCheck := getCreateContainerTestData() | ||||
| 	testSandbox := &sandboxstore.Sandbox{ | ||||
| 		Metadata: sandboxstore.Metadata{ | ||||
| 			ID:     testSandboxID, | ||||
| 			Name:   "test-sandbox-name", | ||||
| 			Config: sandboxConfig, | ||||
| 			Pid:    testSandboxPid, | ||||
| 		}, | ||||
| 	} | ||||
| 	testContainerName := makeContainerName(config.Metadata, sandboxConfig.Metadata) | ||||
| 	// Use an image id to avoid image name resolution. | ||||
| 	// TODO(random-liu): Change this to image name after we have complete image | ||||
| 	// management unit test framework. | ||||
| 	testImageRef := config.GetImage().GetImage() | ||||
| 	testChainID := "test-chain-id" | ||||
| 	testImage := imagestore.Image{ | ||||
| 		ID:      testImageRef, | ||||
| 		ChainID: testChainID, | ||||
| 		Config:  imageConfig, | ||||
| 	} | ||||
|  | ||||
| 	for desc, test := range map[string]struct { | ||||
| 		sandbox            *sandboxstore.Sandbox | ||||
| 		reserveNameErr     bool | ||||
| 		imageStoreErr      bool | ||||
| 		prepareSnapshotErr error | ||||
| 		createRootDirErr   error | ||||
| 		expectErr          bool | ||||
| 		expectedMeta       containerstore.Metadata | ||||
| 	}{ | ||||
| 		"should return error if sandbox does not exist": { | ||||
| 			sandbox:   nil, | ||||
| 			expectErr: true, | ||||
| 		}, | ||||
| 		"should return error if name is reserved": { | ||||
| 			sandbox:        testSandbox, | ||||
| 			reserveNameErr: true, | ||||
| 			expectErr:      true, | ||||
| 		}, | ||||
| 		"should return error if fail to create root directory": { | ||||
| 			sandbox:          testSandbox, | ||||
| 			createRootDirErr: errors.New("random error"), | ||||
| 			expectErr:        true, | ||||
| 		}, | ||||
| 		"should return error if image is not pulled": { | ||||
| 			sandbox:       testSandbox, | ||||
| 			imageStoreErr: true, | ||||
| 			expectErr:     true, | ||||
| 		}, | ||||
| 		"should return error if prepare snapshot fails": { | ||||
| 			sandbox:            testSandbox, | ||||
| 			prepareSnapshotErr: errors.New("random error"), | ||||
| 			expectErr:          true, | ||||
| 		}, | ||||
| 		"should be able to create container successfully": { | ||||
| 			sandbox:   testSandbox, | ||||
| 			expectErr: false, | ||||
| 			expectedMeta: containerstore.Metadata{ | ||||
| 				Name:      testContainerName, | ||||
| 				SandboxID: testSandboxID, | ||||
| 				ImageRef:  testImageRef, | ||||
| 				Config:    config, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} { | ||||
| 		t.Logf("TestCase %q", desc) | ||||
| 		c := newTestCRIContainerdService() | ||||
| 		fake := c.containerService.(*servertesting.FakeContainersClient) | ||||
| 		fakeSnapshotClient := WithFakeSnapshotClient(c) | ||||
| 		fakeOS := c.os.(*ostesting.FakeOS) | ||||
| 		if test.sandbox != nil { | ||||
| 			assert.NoError(t, c.sandboxStore.Add(*test.sandbox)) | ||||
| 		} | ||||
| 		if test.reserveNameErr { | ||||
| 			assert.NoError(t, c.containerNameIndex.Reserve(testContainerName, "random id")) | ||||
| 		} | ||||
| 		if !test.imageStoreErr { | ||||
| 			c.imageStore.Add(testImage) | ||||
| 		} | ||||
| 		if test.prepareSnapshotErr != nil { | ||||
| 			fakeSnapshotClient.InjectError("prepare", test.prepareSnapshotErr) | ||||
| 		} | ||||
| 		rootExists := false | ||||
| 		rootPath := "" | ||||
| 		fakeOS.MkdirAllFn = func(path string, perm os.FileMode) error { | ||||
| 			assert.Equal(t, os.FileMode(0755), perm) | ||||
| 			rootPath = path | ||||
| 			if test.createRootDirErr == nil { | ||||
| 				rootExists = true | ||||
| 			} | ||||
| 			return test.createRootDirErr | ||||
| 		} | ||||
| 		fakeOS.RemoveAllFn = func(path string) error { | ||||
| 			assert.Equal(t, rootPath, path) | ||||
| 			rootExists = false | ||||
| 			return nil | ||||
| 		} | ||||
| 		resp, err := c.CreateContainer(context.Background(), &runtime.CreateContainerRequest{ | ||||
| 			PodSandboxId:  testSandboxID, | ||||
| 			Config:        config, | ||||
| 			SandboxConfig: sandboxConfig, | ||||
| 		}) | ||||
| 		if test.expectErr { | ||||
| 			assert.Error(t, err) | ||||
| 			assert.Nil(t, resp) | ||||
| 			assert.False(t, rootExists, "root directory should be cleaned up") | ||||
| 			if !test.reserveNameErr { | ||||
| 				assert.NoError(t, c.containerNameIndex.Reserve(testContainerName, "random id"), | ||||
| 					"container name should be released") | ||||
| 			} | ||||
| 			assert.Empty(t, fakeSnapshotClient.ListMounts(), "snapshot should be cleaned up") | ||||
| 			listResp, err := fake.List(context.Background(), &containers.ListContainersRequest{}) | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.Empty(t, listResp.Containers, "containerd container should be cleaned up") | ||||
| 			assert.Empty(t, c.containerStore.List(), "container metadata should not be created") | ||||
| 			continue | ||||
| 		} | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.NotNil(t, resp) | ||||
| 		id := resp.GetContainerId() | ||||
| 		assert.True(t, rootExists) | ||||
| 		assert.Equal(t, getContainerRootDir(c.rootDir, id), rootPath, "root directory should be created") | ||||
|  | ||||
| 		// Check runtime spec | ||||
| 		containersCalls := fake.GetCalledDetails() | ||||
| 		require.Len(t, containersCalls, 1) | ||||
| 		createOpts, ok := containersCalls[0].Argument.(*containers.CreateContainerRequest) | ||||
| 		assert.True(t, ok, "should create containerd container") | ||||
| 		assert.Equal(t, id, createOpts.Container.ID, "container id should be correct") | ||||
| 		assert.Equal(t, testImageRef, createOpts.Container.Image, "test image should be correct") | ||||
| 		assert.Equal(t, id, createOpts.Container.RootFS, "rootfs should be correct") | ||||
| 		spec := &runtimespec.Spec{} | ||||
| 		assert.NoError(t, json.Unmarshal(createOpts.Container.Spec.Value, spec)) | ||||
| 		specCheck(t, id, testSandboxPid, spec) | ||||
|  | ||||
| 		assert.Equal(t, []string{"prepare"}, fakeSnapshotClient.GetCalledNames(), "prepare should be called") | ||||
| 		snapshotCalls := fakeSnapshotClient.GetCalledDetails() | ||||
| 		require.Len(t, snapshotCalls, 1) | ||||
| 		prepareOpts := snapshotCalls[0].Argument.(*snapshotapi.PrepareRequest) | ||||
| 		assert.Equal(t, &snapshotapi.PrepareRequest{ | ||||
| 			Key:    id, | ||||
| 			Parent: testChainID, | ||||
| 		}, prepareOpts, "prepare request should be correct") | ||||
|  | ||||
| 		container, err := c.containerStore.Get(id) | ||||
| 		assert.NoError(t, err) | ||||
| 		test.expectedMeta.ID = id | ||||
| 		assert.Equal(t, test.expectedMeta, container.Metadata, "container metadata should be created") | ||||
| 		assert.Equal(t, runtime.ContainerState_CONTAINER_CREATED, container.Status.Get().State(), | ||||
| 			"container should be in created state") | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -17,20 +17,11 @@ limitations under the License. | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd/api/services/containers" | ||||
| 	snapshotapi "github.com/containerd/containerd/api/services/snapshot" | ||||
| 	"github.com/containerd/containerd/api/types/mount" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" | ||||
|  | ||||
| 	ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing" | ||||
| 	servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing" | ||||
| 	"github.com/kubernetes-incubator/cri-containerd/pkg/store" | ||||
| 	containerstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/container" | ||||
| ) | ||||
|  | ||||
| @@ -83,146 +74,3 @@ func TestSetContainerRemoving(t *testing.T) { | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestRemoveContainer(t *testing.T) { | ||||
| 	testID := "test-id" | ||||
| 	testName := "test-name" | ||||
| 	testContainerStatus := &containerstore.Status{ | ||||
| 		CreatedAt:  time.Now().UnixNano(), | ||||
| 		StartedAt:  time.Now().UnixNano(), | ||||
| 		FinishedAt: time.Now().UnixNano(), | ||||
| 	} | ||||
|  | ||||
| 	for desc, test := range map[string]struct { | ||||
| 		status              *containerstore.Status | ||||
| 		removeSnapshotErr   error | ||||
| 		deleteContainerErr  error | ||||
| 		removeDirErr        error | ||||
| 		expectErr           bool | ||||
| 		expectUnsetRemoving bool | ||||
| 	}{ | ||||
| 		"should return error when container is still running": { | ||||
| 			status: &containerstore.Status{ | ||||
| 				CreatedAt: time.Now().UnixNano(), | ||||
| 				StartedAt: time.Now().UnixNano(), | ||||
| 			}, | ||||
| 			expectErr: true, | ||||
| 		}, | ||||
| 		"should return error when there is ongoing removing": { | ||||
| 			status: &containerstore.Status{ | ||||
| 				CreatedAt:  time.Now().UnixNano(), | ||||
| 				StartedAt:  time.Now().UnixNano(), | ||||
| 				FinishedAt: time.Now().UnixNano(), | ||||
| 				Removing:   true, | ||||
| 			}, | ||||
| 			expectErr: true, | ||||
| 		}, | ||||
| 		"should not return error if container does not exist": { | ||||
| 			status:             nil, | ||||
| 			removeSnapshotErr:  servertesting.SnapshotNotExistError, | ||||
| 			deleteContainerErr: servertesting.ContainerNotExistError, | ||||
| 			expectErr:          false, | ||||
| 		}, | ||||
| 		"should not return error if snapshot does not exist": { | ||||
| 			status:            testContainerStatus, | ||||
| 			removeSnapshotErr: servertesting.SnapshotNotExistError, | ||||
| 			expectErr:         false, | ||||
| 		}, | ||||
| 		"should return error if remove snapshot fails": { | ||||
| 			status:            testContainerStatus, | ||||
| 			removeSnapshotErr: errors.New("random error"), | ||||
| 			expectErr:         true, | ||||
| 		}, | ||||
| 		"should not return error if containerd container does not exist": { | ||||
| 			status:             testContainerStatus, | ||||
| 			deleteContainerErr: servertesting.ContainerNotExistError, | ||||
| 			expectErr:          false, | ||||
| 		}, | ||||
| 		"should return error if delete containerd container fails": { | ||||
| 			status:             testContainerStatus, | ||||
| 			deleteContainerErr: errors.New("random error"), | ||||
| 			expectErr:          true, | ||||
| 		}, | ||||
| 		"should return error if remove container root fails": { | ||||
| 			status:              testContainerStatus, | ||||
| 			removeDirErr:        errors.New("random error"), | ||||
| 			expectErr:           true, | ||||
| 			expectUnsetRemoving: true, | ||||
| 		}, | ||||
| 		"should be able to remove container successfully": { | ||||
| 			status:    testContainerStatus, | ||||
| 			expectErr: false, | ||||
| 		}, | ||||
| 	} { | ||||
| 		t.Logf("TestCase %q", desc) | ||||
| 		c := newTestCRIContainerdService() | ||||
| 		fake := c.containerService.(*servertesting.FakeContainersClient) | ||||
| 		fakeSnapshotClient := WithFakeSnapshotClient(c) | ||||
| 		fakeOS := c.os.(*ostesting.FakeOS) | ||||
| 		if test.status != nil { | ||||
| 			assert.NoError(t, c.containerNameIndex.Reserve(testName, testID)) | ||||
| 			container, err := containerstore.NewContainer( | ||||
| 				containerstore.Metadata{ID: testID}, | ||||
| 				*test.status, | ||||
| 			) | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.NoError(t, c.containerStore.Add(container)) | ||||
| 		} | ||||
| 		fakeOS.RemoveAllFn = func(path string) error { | ||||
| 			assert.Equal(t, getContainerRootDir(c.rootDir, testID), path) | ||||
| 			return test.removeDirErr | ||||
| 		} | ||||
| 		if test.removeSnapshotErr == nil { | ||||
| 			fakeSnapshotClient.SetFakeMounts(testID, []*mount.Mount{ | ||||
| 				{ | ||||
| 					Type:   "bind", | ||||
| 					Source: "/test/source", | ||||
| 					Target: "/test/target", | ||||
| 				}, | ||||
| 			}) | ||||
| 		} else { | ||||
| 			fakeSnapshotClient.InjectError("remove", test.removeSnapshotErr) | ||||
| 		} | ||||
| 		if test.deleteContainerErr == nil { | ||||
| 			_, err := fake.Create(context.Background(), &containers.CreateContainerRequest{ | ||||
| 				Container: containers.Container{ID: testID}, | ||||
| 			}) | ||||
| 			assert.NoError(t, err) | ||||
| 		} else { | ||||
| 			fake.InjectError("delete", test.deleteContainerErr) | ||||
| 		} | ||||
| 		resp, err := c.RemoveContainer(context.Background(), &runtime.RemoveContainerRequest{ | ||||
| 			ContainerId: testID, | ||||
| 		}) | ||||
| 		if test.expectErr { | ||||
| 			assert.Error(t, err) | ||||
| 			assert.Nil(t, resp) | ||||
| 			if !test.expectUnsetRemoving { | ||||
| 				continue | ||||
| 			} | ||||
| 			container, err := c.containerStore.Get(testID) | ||||
| 			assert.NoError(t, err) | ||||
| 			// Also covers resetContainerRemoving. | ||||
| 			assert.False(t, container.Status.Get().Removing, "removing state should be unset") | ||||
| 			continue | ||||
| 		} | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.NotNil(t, resp) | ||||
| 		_, err = c.containerStore.Get(testID) | ||||
| 		assert.Equal(t, store.ErrNotExist, err) | ||||
| 		assert.NoError(t, c.containerNameIndex.Reserve(testName, testID), | ||||
| 			"container name should be released") | ||||
| 		mountsResp, err := fakeSnapshotClient.Mounts(context.Background(), &snapshotapi.MountsRequest{Key: testID}) | ||||
| 		assert.Equal(t, servertesting.SnapshotNotExistError, err, "snapshot should be removed") | ||||
| 		assert.Nil(t, mountsResp) | ||||
| 		getResp, err := fake.Get(context.Background(), &containers.GetContainerRequest{ID: testID}) | ||||
| 		assert.Equal(t, servertesting.ContainerNotExistError, err, "containerd container should be removed") | ||||
| 		assert.Nil(t, getResp) | ||||
|  | ||||
| 		resp, err = c.RemoveContainer(context.Background(), &runtime.RemoveContainerRequest{ | ||||
| 			ContainerId: testID, | ||||
| 		}) | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.NotNil(t, resp, "remove should be idempotent") | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,255 +0,0 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd/api/services/execution" | ||||
| 	"github.com/containerd/containerd/api/types/mount" | ||||
| 	"github.com/containerd/containerd/api/types/task" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" | ||||
|  | ||||
| 	ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing" | ||||
| 	servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing" | ||||
| 	containerstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/container" | ||||
| 	sandboxstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/sandbox" | ||||
| ) | ||||
|  | ||||
| func TestStartContainer(t *testing.T) { | ||||
| 	testID := "test-id" | ||||
| 	testSandboxID := "test-sandbox-id" | ||||
| 	testMetadata := containerstore.Metadata{ | ||||
| 		ID:        testID, | ||||
| 		Name:      "test-name", | ||||
| 		SandboxID: testSandboxID, | ||||
| 	} | ||||
| 	testStatus := &containerstore.Status{CreatedAt: time.Now().UnixNano()} | ||||
| 	testSandbox := &sandboxstore.Sandbox{ | ||||
| 		Metadata: sandboxstore.Metadata{ | ||||
| 			ID:   testSandboxID, | ||||
| 			Name: "test-sandbox-name", | ||||
| 		}, | ||||
| 	} | ||||
| 	testSandboxContainer := &task.Task{ | ||||
| 		ID:     testSandboxID, | ||||
| 		Pid:    uint32(4321), | ||||
| 		Status: task.StatusRunning, | ||||
| 	} | ||||
| 	testMounts := []*mount.Mount{{Type: "bind", Source: "test-source"}} | ||||
| 	for desc, test := range map[string]struct { | ||||
| 		status                     *containerstore.Status | ||||
| 		sandbox                    *sandboxstore.Sandbox | ||||
| 		sandboxContainerdContainer *task.Task | ||||
| 		snapshotMountsErr          bool | ||||
| 		prepareFIFOErr             error | ||||
| 		createContainerErr         error | ||||
| 		startContainerErr          error | ||||
| 		expectStateChange          bool | ||||
| 		expectCalls                []string | ||||
| 		expectErr                  bool | ||||
| 	}{ | ||||
| 		"should return error when container does not exist": { | ||||
| 			status:                     nil, | ||||
| 			sandbox:                    testSandbox, | ||||
| 			sandboxContainerdContainer: testSandboxContainer, | ||||
| 			expectCalls:                []string{}, | ||||
| 			expectErr:                  true, | ||||
| 		}, | ||||
| 		"should return error when container is not in created state": { | ||||
| 			status: &containerstore.Status{ | ||||
| 				CreatedAt: time.Now().UnixNano(), | ||||
| 				StartedAt: time.Now().UnixNano(), | ||||
| 			}, | ||||
| 			sandbox:                    testSandbox, | ||||
| 			sandboxContainerdContainer: testSandboxContainer, | ||||
| 			expectCalls:                []string{}, | ||||
| 			expectErr:                  true, | ||||
| 		}, | ||||
| 		"should return error when container is in removing state": { | ||||
| 			status: &containerstore.Status{ | ||||
| 				CreatedAt: time.Now().UnixNano(), | ||||
| 				Removing:  true, | ||||
| 			}, | ||||
| 			sandbox:                    testSandbox, | ||||
| 			sandboxContainerdContainer: testSandboxContainer, | ||||
| 			expectCalls:                []string{}, | ||||
| 			expectErr:                  true, | ||||
| 		}, | ||||
| 		"should return error when sandbox does not exist": { | ||||
| 			status:                     testStatus, | ||||
| 			sandbox:                    nil, | ||||
| 			sandboxContainerdContainer: testSandboxContainer, | ||||
| 			expectStateChange:          true, | ||||
| 			expectCalls:                []string{}, | ||||
| 			expectErr:                  true, | ||||
| 		}, | ||||
| 		"should return error when sandbox is not running": { | ||||
| 			status:  testStatus, | ||||
| 			sandbox: testSandbox, | ||||
| 			sandboxContainerdContainer: &task.Task{ | ||||
| 				ID:     testSandboxID, | ||||
| 				Pid:    uint32(4321), | ||||
| 				Status: task.StatusStopped, | ||||
| 			}, | ||||
| 			expectStateChange: true, | ||||
| 			expectCalls:       []string{"info"}, | ||||
| 			expectErr:         true, | ||||
| 		}, | ||||
| 		"should return error when snapshot mounts fails": { | ||||
| 			status:                     testStatus, | ||||
| 			sandbox:                    testSandbox, | ||||
| 			sandboxContainerdContainer: testSandboxContainer, | ||||
| 			snapshotMountsErr:          true, | ||||
| 			expectStateChange:          true, | ||||
| 			expectCalls:                []string{"info"}, | ||||
| 			expectErr:                  true, | ||||
| 		}, | ||||
| 		"should return error when fail to open streaming pipes": { | ||||
| 			status:                     testStatus, | ||||
| 			sandbox:                    testSandbox, | ||||
| 			sandboxContainerdContainer: testSandboxContainer, | ||||
| 			prepareFIFOErr:             errors.New("open error"), | ||||
| 			expectStateChange:          true, | ||||
| 			expectCalls:                []string{"info"}, | ||||
| 			expectErr:                  true, | ||||
| 		}, | ||||
| 		"should return error when fail to create container": { | ||||
| 			status:                     testStatus, | ||||
| 			sandbox:                    testSandbox, | ||||
| 			sandboxContainerdContainer: testSandboxContainer, | ||||
| 			createContainerErr:         errors.New("create error"), | ||||
| 			expectStateChange:          true, | ||||
| 			expectCalls:                []string{"info", "create"}, | ||||
| 			expectErr:                  true, | ||||
| 		}, | ||||
| 		"should return error when fail to start container": { | ||||
| 			status:                     testStatus, | ||||
| 			sandbox:                    testSandbox, | ||||
| 			sandboxContainerdContainer: testSandboxContainer, | ||||
| 			startContainerErr:          errors.New("start error"), | ||||
| 			expectStateChange:          true, | ||||
| 			// cleanup the containerd task. | ||||
| 			expectCalls: []string{"info", "create", "start", "delete"}, | ||||
| 			expectErr:   true, | ||||
| 		}, | ||||
| 		"should be able to start container successfully": { | ||||
| 			status:                     testStatus, | ||||
| 			sandbox:                    testSandbox, | ||||
| 			sandboxContainerdContainer: testSandboxContainer, | ||||
| 			expectStateChange:          true, | ||||
| 			expectCalls:                []string{"info", "create", "start"}, | ||||
| 			expectErr:                  false, | ||||
| 		}, | ||||
| 	} { | ||||
| 		t.Logf("TestCase %q", desc) | ||||
| 		c := newTestCRIContainerdService() | ||||
| 		fake := c.taskService.(*servertesting.FakeExecutionClient) | ||||
| 		fakeOS := c.os.(*ostesting.FakeOS) | ||||
| 		fakeSnapshotClient := WithFakeSnapshotClient(c) | ||||
| 		if test.status != nil { | ||||
| 			cntr, err := containerstore.NewContainer( | ||||
| 				testMetadata, | ||||
| 				*test.status, | ||||
| 			) | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.NoError(t, c.containerStore.Add(cntr)) | ||||
| 		} | ||||
| 		if test.sandbox != nil { | ||||
| 			assert.NoError(t, c.sandboxStore.Add(*test.sandbox)) | ||||
| 		} | ||||
| 		if test.sandboxContainerdContainer != nil { | ||||
| 			fake.SetFakeTasks([]task.Task{*test.sandboxContainerdContainer}) | ||||
| 		} | ||||
| 		if !test.snapshotMountsErr { | ||||
| 			fakeSnapshotClient.SetFakeMounts(testID, testMounts) | ||||
| 		} | ||||
| 		// TODO(random-liu): Test behavior with different streaming config. | ||||
| 		fakeOS.OpenFifoFn = func(context.Context, string, int, os.FileMode) (io.ReadWriteCloser, error) { | ||||
| 			return nopReadWriteCloser{}, test.prepareFIFOErr | ||||
| 		} | ||||
| 		if test.createContainerErr != nil { | ||||
| 			fake.InjectError("create", test.createContainerErr) | ||||
| 		} | ||||
| 		if test.startContainerErr != nil { | ||||
| 			fake.InjectError("start", test.startContainerErr) | ||||
| 		} | ||||
| 		resp, err := c.StartContainer(context.Background(), &runtime.StartContainerRequest{ | ||||
| 			ContainerId: testID, | ||||
| 		}) | ||||
| 		// Check containerd functions called. | ||||
| 		assert.Equal(t, test.expectCalls, fake.GetCalledNames()) | ||||
| 		// Check results returned. | ||||
| 		if test.expectErr { | ||||
| 			assert.Error(t, err) | ||||
| 			assert.Nil(t, resp) | ||||
| 		} else { | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.NotNil(t, resp) | ||||
| 		} | ||||
| 		// Skip following validation if no container is injected initially. | ||||
| 		if test.status == nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		// Check container state. | ||||
| 		cntr, err := c.containerStore.Get(testID) | ||||
| 		assert.NoError(t, err) | ||||
| 		status := cntr.Status.Get() | ||||
| 		if !test.expectStateChange { | ||||
| 			assert.Equal(t, testMetadata, cntr.Metadata) | ||||
| 			assert.Equal(t, *test.status, status) | ||||
| 			continue | ||||
| 		} | ||||
| 		if test.expectErr { | ||||
| 			t.Logf("container state should be in exited state when fail to start") | ||||
| 			assert.Equal(t, runtime.ContainerState_CONTAINER_EXITED, status.State()) | ||||
| 			assert.Zero(t, status.Pid) | ||||
| 			assert.EqualValues(t, errorStartExitCode, status.ExitCode) | ||||
| 			assert.Equal(t, errorStartReason, status.Reason) | ||||
| 			assert.NotEmpty(t, status.Message) | ||||
| 			_, err := fake.Info(context.Background(), &execution.InfoRequest{ContainerID: testID}) | ||||
| 			assert.True(t, isContainerdGRPCNotFoundError(err), | ||||
| 				"containerd task should be cleaned up when fail to start") | ||||
| 			continue | ||||
| 		} | ||||
| 		t.Logf("container state should be running when start successfully") | ||||
| 		assert.Equal(t, runtime.ContainerState_CONTAINER_RUNNING, status.State()) | ||||
| 		info, err := fake.Info(context.Background(), &execution.InfoRequest{ContainerID: testID}) | ||||
| 		assert.NoError(t, err) | ||||
| 		pid := info.Task.Pid | ||||
| 		assert.Equal(t, pid, status.Pid) | ||||
| 		assert.Equal(t, task.StatusRunning, info.Task.Status) | ||||
| 		// Check runtime spec | ||||
| 		calls := fake.GetCalledDetails() | ||||
| 		createOpts, ok := calls[1].Argument.(*execution.CreateRequest) | ||||
| 		assert.True(t, ok, "2nd call should be create") | ||||
| 		_, stdout, stderr := getStreamingPipes(getContainerRootDir(c.rootDir, testID)) | ||||
| 		assert.Equal(t, &execution.CreateRequest{ | ||||
| 			ContainerID: testID, | ||||
| 			Rootfs:      testMounts, | ||||
| 			// TODO(random-liu): Test streaming configurations. | ||||
| 			Stdout: stdout, | ||||
| 			Stderr: stderr, | ||||
| 		}, createOpts, "create options should be correct") | ||||
| 	} | ||||
| } | ||||
| @@ -17,21 +17,13 @@ limitations under the License. | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd/api/services/execution" | ||||
| 	"github.com/containerd/containerd/api/types/task" | ||||
| 	imagespec "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"golang.org/x/sys/unix" | ||||
| 	"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" | ||||
|  | ||||
| 	servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing" | ||||
| 	containerstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/container" | ||||
| 	imagestore "github.com/kubernetes-incubator/cri-containerd/pkg/store/image" | ||||
| ) | ||||
|  | ||||
| func TestWaitContainerStop(t *testing.T) { | ||||
| @@ -93,240 +85,3 @@ func TestWaitContainerStop(t *testing.T) { | ||||
| 		assert.Equal(t, test.expectErr, err != nil, desc) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestStopContainer(t *testing.T) { | ||||
| 	testID := "test-id" | ||||
| 	testPid := uint32(1234) | ||||
| 	testImageID := "test-image-id" | ||||
| 	testStatus := containerstore.Status{ | ||||
| 		Pid:       testPid, | ||||
| 		CreatedAt: time.Now().UnixNano(), | ||||
| 		StartedAt: time.Now().UnixNano(), | ||||
| 	} | ||||
| 	testImage := imagestore.Image{ | ||||
| 		ID:     testImageID, | ||||
| 		Config: &imagespec.ImageConfig{}, | ||||
| 	} | ||||
| 	testContainer := task.Task{ | ||||
| 		ID:     testID, | ||||
| 		Pid:    testPid, | ||||
| 		Status: task.StatusRunning, | ||||
| 	} | ||||
| 	for desc, test := range map[string]struct { | ||||
| 		status              *containerstore.Status | ||||
| 		containerdContainer *task.Task | ||||
| 		stopSignal          string | ||||
| 		stopErr             error | ||||
| 		noTimeout           bool | ||||
| 		expectErr           bool | ||||
| 		expectCalls         []servertesting.CalledDetail | ||||
| 	}{ | ||||
| 		"should return error when container does not exist": { | ||||
| 			status:      nil, | ||||
| 			expectErr:   true, | ||||
| 			expectCalls: []servertesting.CalledDetail{}, | ||||
| 		}, | ||||
| 		"should not return error when container is not running": { | ||||
| 			status:      &containerstore.Status{CreatedAt: time.Now().UnixNano()}, | ||||
| 			expectErr:   false, | ||||
| 			expectCalls: []servertesting.CalledDetail{}, | ||||
| 		}, | ||||
| 		"should not return error if containerd task does not exist": { | ||||
| 			status:              &testStatus, | ||||
| 			containerdContainer: &testContainer, | ||||
| 			// Since it's hard to inject event during `StopContainer` is running, | ||||
| 			// we only test the case that first stop returns error, but container | ||||
| 			// status is not updated yet. | ||||
| 			// We also leverage this behavior to test that when graceful | ||||
| 			// stop doesn't take effect, container should be SIGKILL-ed. | ||||
| 			stopErr:   servertesting.TaskNotExistError, | ||||
| 			expectErr: false, | ||||
| 			expectCalls: []servertesting.CalledDetail{ | ||||
| 				{ | ||||
| 					Name: "kill", | ||||
| 					Argument: &execution.KillRequest{ | ||||
| 						ContainerID: testID, | ||||
| 						Signal:      uint32(unix.SIGTERM), | ||||
| 						PidOrAll:    &execution.KillRequest_All{All: true}, | ||||
| 					}, | ||||
| 				}, | ||||
| 				{ | ||||
| 					Name: "kill", | ||||
| 					Argument: &execution.KillRequest{ | ||||
| 						ContainerID: testID, | ||||
| 						Signal:      uint32(unix.SIGKILL), | ||||
| 						PidOrAll:    &execution.KillRequest_All{All: true}, | ||||
| 					}, | ||||
| 				}, | ||||
| 				{ | ||||
| 					Name:     "delete", | ||||
| 					Argument: &execution.DeleteRequest{ContainerID: testID}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		"should not return error if containerd task process already finished": { | ||||
| 			status:              &testStatus, | ||||
| 			containerdContainer: &testContainer, | ||||
| 			stopErr:             errors.New("os: process already finished"), | ||||
| 			expectErr:           false, | ||||
| 			expectCalls: []servertesting.CalledDetail{ | ||||
| 				{ | ||||
| 					Name: "kill", | ||||
| 					Argument: &execution.KillRequest{ | ||||
| 						ContainerID: testID, | ||||
| 						Signal:      uint32(unix.SIGTERM), | ||||
| 						PidOrAll:    &execution.KillRequest_All{All: true}, | ||||
| 					}, | ||||
| 				}, | ||||
| 				{ | ||||
| 					Name: "kill", | ||||
| 					Argument: &execution.KillRequest{ | ||||
| 						ContainerID: testID, | ||||
| 						Signal:      uint32(unix.SIGKILL), | ||||
| 						PidOrAll:    &execution.KillRequest_All{All: true}, | ||||
| 					}, | ||||
| 				}, | ||||
| 				{ | ||||
| 					Name:     "delete", | ||||
| 					Argument: &execution.DeleteRequest{ContainerID: testID}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		"should return error if graceful stop returns random error": { | ||||
| 			status:              &testStatus, | ||||
| 			containerdContainer: &testContainer, | ||||
| 			stopErr:             errors.New("random stop error"), | ||||
| 			expectErr:           true, | ||||
| 			expectCalls: []servertesting.CalledDetail{ | ||||
| 				{ | ||||
| 					Name: "kill", | ||||
| 					Argument: &execution.KillRequest{ | ||||
| 						ContainerID: testID, | ||||
| 						Signal:      uint32(unix.SIGTERM), | ||||
| 						PidOrAll:    &execution.KillRequest_All{All: true}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		"should not return error if containerd task is gracefully stopped": { | ||||
| 			status:              &testStatus, | ||||
| 			containerdContainer: &testContainer, | ||||
| 			expectErr:           false, | ||||
| 			// deleted by the event monitor. | ||||
| 			expectCalls: []servertesting.CalledDetail{ | ||||
| 				{ | ||||
| 					Name: "kill", | ||||
| 					Argument: &execution.KillRequest{ | ||||
| 						ContainerID: testID, | ||||
| 						Signal:      uint32(unix.SIGTERM), | ||||
| 						PidOrAll:    &execution.KillRequest_All{All: true}, | ||||
| 					}, | ||||
| 				}, | ||||
| 				{ | ||||
| 					Name:     "delete", | ||||
| 					Argument: &execution.DeleteRequest{ContainerID: testID}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		"should use stop signal specified in image config if not empty": { | ||||
| 			status:              &testStatus, | ||||
| 			containerdContainer: &testContainer, | ||||
| 			stopSignal:          "SIGHUP", | ||||
| 			expectErr:           false, | ||||
| 			// deleted by the event monitor. | ||||
| 			expectCalls: []servertesting.CalledDetail{ | ||||
| 				{ | ||||
| 					Name: "kill", | ||||
| 					Argument: &execution.KillRequest{ | ||||
| 						ContainerID: testID, | ||||
| 						Signal:      uint32(unix.SIGHUP), | ||||
| 						PidOrAll:    &execution.KillRequest_All{All: true}, | ||||
| 					}, | ||||
| 				}, | ||||
| 				{ | ||||
| 					Name:     "delete", | ||||
| 					Argument: &execution.DeleteRequest{ContainerID: testID}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		"should directly kill container if timeout is 0": { | ||||
| 			status:              &testStatus, | ||||
| 			containerdContainer: &testContainer, | ||||
| 			noTimeout:           true, | ||||
| 			expectErr:           false, | ||||
| 			expectCalls: []servertesting.CalledDetail{ | ||||
| 				{ | ||||
| 					Name: "kill", | ||||
| 					Argument: &execution.KillRequest{ | ||||
| 						ContainerID: testID, | ||||
| 						Signal:      uint32(unix.SIGKILL), | ||||
| 						PidOrAll:    &execution.KillRequest_All{All: true}, | ||||
| 					}, | ||||
| 				}, | ||||
| 				{ | ||||
| 					Name:     "delete", | ||||
| 					Argument: &execution.DeleteRequest{ContainerID: testID}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		// TODO(random-liu): Test "should return error if both failed" after we have | ||||
| 		// fake clock for test. | ||||
| 	} { | ||||
| 		t.Logf("TestCase %q", desc) | ||||
| 		c := newTestCRIContainerdService() | ||||
| 		fake := servertesting.NewFakeExecutionClient().WithEvents() | ||||
| 		defer fake.Stop() | ||||
| 		c.taskService = fake | ||||
|  | ||||
| 		// Inject the container. | ||||
| 		if test.status != nil { | ||||
| 			cntr, err := containerstore.NewContainer( | ||||
| 				containerstore.Metadata{ | ||||
| 					ID:       testID, | ||||
| 					ImageRef: testImageID, | ||||
| 				}, | ||||
| 				*test.status, | ||||
| 			) | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.NoError(t, c.containerStore.Add(cntr)) | ||||
| 		} | ||||
| 		// Inject containerd task. | ||||
| 		if test.containerdContainer != nil { | ||||
| 			fake.SetFakeTasks([]task.Task{*test.containerdContainer}) | ||||
| 		} | ||||
| 		testImage.Config.StopSignal = test.stopSignal | ||||
| 		c.imageStore.Add(testImage) | ||||
| 		if test.stopErr != nil { | ||||
| 			fake.InjectError("kill", test.stopErr) | ||||
| 		} | ||||
| 		eventClient, err := fake.Events(context.Background(), &execution.EventsRequest{}) | ||||
| 		assert.NoError(t, err) | ||||
| 		// Start a simple test event monitor. | ||||
| 		go func(e execution.Tasks_EventsClient) { | ||||
| 			for { | ||||
| 				if err := c.handleEventStream(e); err != nil { // nolint: vetshadow | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
| 		}(eventClient) | ||||
| 		fake.ClearCalls() | ||||
| 		timeout := int64(1) | ||||
| 		if test.noTimeout { | ||||
| 			timeout = 0 | ||||
| 		} | ||||
| 		// 1 second timeout should be enough for the unit test. | ||||
| 		// TODO(random-liu): Use fake clock for this test. | ||||
| 		resp, err := c.StopContainer(context.Background(), &runtime.StopContainerRequest{ | ||||
| 			ContainerId: testID, | ||||
| 			Timeout:     timeout, | ||||
| 		}) | ||||
| 		if test.expectErr { | ||||
| 			assert.Error(t, err) | ||||
| 			assert.Nil(t, resp) | ||||
| 		} else { | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.NotNil(t, resp) | ||||
| 		} | ||||
| 		assert.Equal(t, test.expectCalls, fake.GetCalledDetails()) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,172 +0,0 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd/api/services/execution" | ||||
| 	"github.com/containerd/containerd/api/types/task" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" | ||||
|  | ||||
| 	servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing" | ||||
| 	containerstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/container" | ||||
| ) | ||||
|  | ||||
| func TestHandleEvent(t *testing.T) { | ||||
| 	testID := "test-id" | ||||
| 	testPid := uint32(1234) | ||||
| 	testCreatedAt := time.Now().UnixNano() | ||||
| 	testStartedAt := time.Now().UnixNano() | ||||
| 	testMetadata := containerstore.Metadata{ | ||||
| 		ID:        testID, | ||||
| 		Name:      "test-name", | ||||
| 		SandboxID: "test-sandbox-id", | ||||
| 	} | ||||
| 	// Container status in running state. | ||||
| 	testStatus := containerstore.Status{ | ||||
| 		Pid:       testPid, | ||||
| 		CreatedAt: testCreatedAt, | ||||
| 		StartedAt: testStartedAt, | ||||
| 	} | ||||
| 	testExitedAt := time.Now() | ||||
| 	testExitEvent := task.Event{ | ||||
| 		ID:         testID, | ||||
| 		Type:       task.Event_EXIT, | ||||
| 		Pid:        testPid, | ||||
| 		ExitStatus: 1, | ||||
| 		ExitedAt:   testExitedAt, | ||||
| 	} | ||||
| 	testFinishedStatus := containerstore.Status{ | ||||
| 		Pid:        0, | ||||
| 		CreatedAt:  testCreatedAt, | ||||
| 		StartedAt:  testStartedAt, | ||||
| 		FinishedAt: testExitedAt.UnixNano(), | ||||
| 		ExitCode:   1, | ||||
| 	} | ||||
| 	assert.Equal(t, runtime.ContainerState_CONTAINER_RUNNING, testStatus.State()) | ||||
| 	testContainerdContainer := task.Task{ | ||||
| 		ID:     testID, | ||||
| 		Pid:    testPid, | ||||
| 		Status: task.StatusRunning, | ||||
| 	} | ||||
|  | ||||
| 	for desc, test := range map[string]struct { | ||||
| 		event               *task.Event | ||||
| 		status              *containerstore.Status | ||||
| 		containerdContainer *task.Task | ||||
| 		containerdErr       error | ||||
| 		expected            *containerstore.Status | ||||
| 	}{ | ||||
| 		"should not update state when no corresponding container for event": { | ||||
| 			event:    &testExitEvent, | ||||
| 			expected: nil, | ||||
| 		}, | ||||
| 		"should not update state when exited process is not init process": { | ||||
| 			event: &task.Event{ | ||||
| 				ID:         testID, | ||||
| 				Type:       task.Event_EXIT, | ||||
| 				Pid:        9999, | ||||
| 				ExitStatus: 1, | ||||
| 				ExitedAt:   testExitedAt, | ||||
| 			}, | ||||
| 			status:              &testStatus, | ||||
| 			containerdContainer: &testContainerdContainer, | ||||
| 			expected:            &testStatus, | ||||
| 		}, | ||||
| 		"should not update state when fail to delete containerd task": { | ||||
| 			event:               &testExitEvent, | ||||
| 			status:              &testStatus, | ||||
| 			containerdContainer: &testContainerdContainer, | ||||
| 			containerdErr:       fmt.Errorf("random error"), | ||||
| 			expected:            &testStatus, | ||||
| 		}, | ||||
| 		"should not update state for irrelevant events": { | ||||
| 			event: &task.Event{ | ||||
| 				ID:   testID, | ||||
| 				Type: task.Event_PAUSED, | ||||
| 				Pid:  testPid, | ||||
| 			}, | ||||
| 			status:              &testStatus, | ||||
| 			containerdContainer: &testContainerdContainer, | ||||
| 			expected:            &testStatus, | ||||
| 		}, | ||||
| 		"should update state when containerd task is already deleted": { | ||||
| 			event:    &testExitEvent, | ||||
| 			status:   &testStatus, | ||||
| 			expected: &testFinishedStatus, | ||||
| 		}, | ||||
| 		"should update state when delete containerd task successfully": { | ||||
| 			event:               &testExitEvent, | ||||
| 			status:              &testStatus, | ||||
| 			containerdContainer: &testContainerdContainer, | ||||
| 			expected:            &testFinishedStatus, | ||||
| 		}, | ||||
| 		"should update exit reason when container is oom killed": { | ||||
| 			event: &task.Event{ | ||||
| 				ID:   testID, | ||||
| 				Type: task.Event_OOM, | ||||
| 			}, | ||||
| 			status: &testStatus, | ||||
| 			expected: &containerstore.Status{ | ||||
| 				Pid:       testPid, | ||||
| 				CreatedAt: testCreatedAt, | ||||
| 				StartedAt: testStartedAt, | ||||
| 				Reason:    oomExitReason, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} { | ||||
| 		t.Logf("TestCase %q", desc) | ||||
| 		c := newTestCRIContainerdService() | ||||
| 		fake := c.taskService.(*servertesting.FakeExecutionClient) | ||||
| 		e, err := fake.Events(context.Background(), &execution.EventsRequest{}) | ||||
| 		assert.NoError(t, err) | ||||
| 		fakeEvents := e.(*servertesting.EventClient) | ||||
| 		// Inject event. | ||||
| 		if test.event != nil { | ||||
| 			fakeEvents.Events <- test.event | ||||
| 		} | ||||
| 		// Inject internal container object. | ||||
| 		if test.status != nil { | ||||
| 			cntr, err := containerstore.NewContainer( // nolint: vetshadow | ||||
| 				testMetadata, | ||||
| 				*test.status, | ||||
| 			) | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.NoError(t, c.containerStore.Add(cntr)) | ||||
| 		} | ||||
| 		// Inject containerd task. | ||||
| 		if test.containerdContainer != nil { | ||||
| 			fake.SetFakeTasks([]task.Task{*test.containerdContainer}) | ||||
| 		} | ||||
| 		// Inject containerd delete error. | ||||
| 		if test.containerdErr != nil { | ||||
| 			fake.InjectError("delete", test.containerdErr) | ||||
| 		} | ||||
| 		assert.NoError(t, c.handleEventStream(e)) | ||||
| 		if test.expected == nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		got, err := c.containerStore.Get(testID) | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.Equal(t, *test.expected, got.Status.Get()) | ||||
| 	} | ||||
| } | ||||
| @@ -21,13 +21,8 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"golang.org/x/net/context" | ||||
|  | ||||
| 	"github.com/containerd/containerd/api/types/task" | ||||
|  | ||||
| 	"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" | ||||
|  | ||||
| 	servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing" | ||||
| 	sandboxstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/sandbox" | ||||
| ) | ||||
|  | ||||
| @@ -131,86 +126,3 @@ func TestFilterSandboxes(t *testing.T) { | ||||
| 		assert.Equal(t, test.expect, filtered, desc) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestListPodSandbox(t *testing.T) { | ||||
| 	c := newTestCRIContainerdService() | ||||
|  | ||||
| 	fake := c.taskService.(*servertesting.FakeExecutionClient) | ||||
|  | ||||
| 	sandboxesInStore := []sandboxstore.Sandbox{ | ||||
| 		{ | ||||
| 			Metadata: sandboxstore.Metadata{ | ||||
| 				ID:     "1", | ||||
| 				Name:   "name-1", | ||||
| 				Config: &runtime.PodSandboxConfig{Metadata: &runtime.PodSandboxMetadata{Name: "name-1"}}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Metadata: sandboxstore.Metadata{ | ||||
| 				ID:     "2", | ||||
| 				Name:   "name-2", | ||||
| 				Config: &runtime.PodSandboxConfig{Metadata: &runtime.PodSandboxMetadata{Name: "name-2"}}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Metadata: sandboxstore.Metadata{ | ||||
| 				ID:     "3", | ||||
| 				Name:   "name-3", | ||||
| 				Config: &runtime.PodSandboxConfig{Metadata: &runtime.PodSandboxMetadata{Name: "name-3"}}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	sandboxesInContainerd := []task.Task{ | ||||
| 		// Running container with corresponding metadata | ||||
| 		{ | ||||
| 			ID:     "1", | ||||
| 			Pid:    1, | ||||
| 			Status: task.StatusRunning, | ||||
| 		}, | ||||
| 		// Stopped container with corresponding metadata | ||||
| 		{ | ||||
| 			ID:     "2", | ||||
| 			Pid:    2, | ||||
| 			Status: task.StatusStopped, | ||||
| 		}, | ||||
| 		// Container without corresponding metadata | ||||
| 		{ | ||||
| 			ID:     "4", | ||||
| 			Pid:    4, | ||||
| 			Status: task.StatusStopped, | ||||
| 		}, | ||||
| 	} | ||||
| 	expect := []*runtime.PodSandbox{ | ||||
| 		{ | ||||
| 			Id:       "1", | ||||
| 			Metadata: &runtime.PodSandboxMetadata{Name: "name-1"}, | ||||
| 			State:    runtime.PodSandboxState_SANDBOX_READY, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Id:       "2", | ||||
| 			Metadata: &runtime.PodSandboxMetadata{Name: "name-2"}, | ||||
| 			State:    runtime.PodSandboxState_SANDBOX_NOTREADY, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Id:       "3", | ||||
| 			Metadata: &runtime.PodSandboxMetadata{Name: "name-3"}, | ||||
| 			State:    runtime.PodSandboxState_SANDBOX_NOTREADY, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	// Inject test metadata | ||||
| 	for _, s := range sandboxesInStore { | ||||
| 		assert.NoError(t, c.sandboxStore.Add(s)) | ||||
| 	} | ||||
|  | ||||
| 	// Inject fake containerd tasks | ||||
| 	fake.SetFakeTasks(sandboxesInContainerd) | ||||
|  | ||||
| 	resp, err := c.ListPodSandbox(context.Background(), &runtime.ListPodSandboxRequest{}) | ||||
| 	assert.NoError(t, err) | ||||
| 	sandboxes := resp.GetItems() | ||||
| 	assert.Len(t, sandboxes, len(expect)) | ||||
| 	for _, s := range expect { | ||||
| 		assert.Contains(t, sandboxes, s) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,241 +0,0 @@ | ||||
| /* Copyright 2017 The Kubernetes Authors. | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd/api/services/containers" | ||||
| 	snapshotapi "github.com/containerd/containerd/api/services/snapshot" | ||||
| 	"github.com/containerd/containerd/api/types/mount" | ||||
| 	"github.com/containerd/containerd/api/types/task" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" | ||||
|  | ||||
| 	ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing" | ||||
| 	servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing" | ||||
| 	"github.com/kubernetes-incubator/cri-containerd/pkg/store" | ||||
| 	containerstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/container" | ||||
| 	sandboxstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/sandbox" | ||||
| ) | ||||
|  | ||||
| func TestRemovePodSandbox(t *testing.T) { | ||||
| 	testID := "test-id" | ||||
| 	testName := "test-name" | ||||
| 	testSandbox := sandboxstore.Sandbox{ | ||||
| 		Metadata: sandboxstore.Metadata{ | ||||
| 			ID:   testID, | ||||
| 			Name: testName, | ||||
| 		}, | ||||
| 	} | ||||
| 	for desc, test := range map[string]struct { | ||||
| 		sandboxTasks       []task.Task | ||||
| 		injectSandbox      bool | ||||
| 		removeSnapshotErr  error | ||||
| 		deleteContainerErr error | ||||
| 		taskInfoErr        error | ||||
| 		injectFSErr        error | ||||
| 		expectErr          bool | ||||
| 		expectRemoved      string | ||||
| 		expectCalls        []string | ||||
| 	}{ | ||||
| 		"should not return error if sandbox does not exist": { | ||||
| 			injectSandbox:      false, | ||||
| 			removeSnapshotErr:  servertesting.SnapshotNotExistError, | ||||
| 			deleteContainerErr: servertesting.ContainerNotExistError, | ||||
| 			expectErr:          false, | ||||
| 			expectCalls:        []string{}, | ||||
| 		}, | ||||
| 		"should not return error if snapshot does not exist": { | ||||
| 			injectSandbox:     true, | ||||
| 			removeSnapshotErr: servertesting.SnapshotNotExistError, | ||||
| 			expectRemoved:     getSandboxRootDir(testRootDir, testID), | ||||
| 			expectCalls:       []string{"info"}, | ||||
| 		}, | ||||
| 		"should return error if remove snapshot fails": { | ||||
| 			injectSandbox:     true, | ||||
| 			removeSnapshotErr: fmt.Errorf("arbitrary error"), | ||||
| 			expectErr:         true, | ||||
| 			expectCalls:       []string{"info"}, | ||||
| 		}, | ||||
| 		"should return error when sandbox container task is not deleted": { | ||||
| 			injectSandbox: true, | ||||
| 			sandboxTasks:  []task.Task{{ID: testID}}, | ||||
| 			expectErr:     true, | ||||
| 			expectCalls:   []string{"info"}, | ||||
| 		}, | ||||
| 		"should return error when arbitrary containerd error is injected": { | ||||
| 			injectSandbox: true, | ||||
| 			taskInfoErr:   fmt.Errorf("arbitrary error"), | ||||
| 			expectErr:     true, | ||||
| 			expectCalls:   []string{"info"}, | ||||
| 		}, | ||||
| 		"should return error when error fs error is injected": { | ||||
| 			injectSandbox: true, | ||||
| 			injectFSErr:   fmt.Errorf("fs error"), | ||||
| 			expectRemoved: getSandboxRootDir(testRootDir, testID), | ||||
| 			expectErr:     true, | ||||
| 			expectCalls:   []string{"info"}, | ||||
| 		}, | ||||
| 		"should not return error if sandbox container does not exist": { | ||||
| 			injectSandbox:      true, | ||||
| 			deleteContainerErr: servertesting.ContainerNotExistError, | ||||
| 			expectRemoved:      getSandboxRootDir(testRootDir, testID), | ||||
| 			expectCalls:        []string{"info"}, | ||||
| 		}, | ||||
| 		"should return error if delete sandbox container fails": { | ||||
| 			injectSandbox:      true, | ||||
| 			deleteContainerErr: fmt.Errorf("arbitrary error"), | ||||
| 			expectRemoved:      getSandboxRootDir(testRootDir, testID), | ||||
| 			expectErr:          true, | ||||
| 			expectCalls:        []string{"info"}, | ||||
| 		}, | ||||
| 		"should be able to successfully delete": { | ||||
| 			injectSandbox: true, | ||||
| 			expectRemoved: getSandboxRootDir(testRootDir, testID), | ||||
| 			expectCalls:   []string{"info"}, | ||||
| 		}, | ||||
| 	} { | ||||
| 		t.Logf("TestCase %q", desc) | ||||
| 		c := newTestCRIContainerdService() | ||||
| 		fake := c.containerService.(*servertesting.FakeContainersClient) | ||||
| 		fakeOS := c.os.(*ostesting.FakeOS) | ||||
| 		fakeExecutionClient := c.taskService.(*servertesting.FakeExecutionClient) | ||||
| 		fakeSnapshotClient := WithFakeSnapshotClient(c) | ||||
| 		fakeExecutionClient.SetFakeTasks(test.sandboxTasks) | ||||
| 		if test.injectSandbox { | ||||
| 			c.sandboxNameIndex.Reserve(testName, testID) | ||||
| 			assert.NoError(t, c.sandboxStore.Add(testSandbox)) | ||||
| 		} | ||||
| 		if test.removeSnapshotErr == nil { | ||||
| 			fakeSnapshotClient.SetFakeMounts(testID, []*mount.Mount{ | ||||
| 				{ | ||||
| 					Type:   "bind", | ||||
| 					Source: "/test/source", | ||||
| 					Target: "/test/target", | ||||
| 				}, | ||||
| 			}) | ||||
| 		} else { | ||||
| 			fakeSnapshotClient.InjectError("remove", test.removeSnapshotErr) | ||||
| 		} | ||||
| 		if test.deleteContainerErr == nil { | ||||
| 			_, err := fake.Create(context.Background(), &containers.CreateContainerRequest{ | ||||
| 				Container: containers.Container{ID: testID}, | ||||
| 			}) | ||||
| 			assert.NoError(t, err) | ||||
| 		} else { | ||||
| 			fake.InjectError("delete", test.deleteContainerErr) | ||||
| 		} | ||||
| 		if test.taskInfoErr != nil { | ||||
| 			fakeExecutionClient.InjectError("info", test.taskInfoErr) | ||||
| 		} | ||||
| 		fakeOS.RemoveAllFn = func(path string) error { | ||||
| 			assert.Equal(t, test.expectRemoved, path) | ||||
| 			return test.injectFSErr | ||||
| 		} | ||||
| 		res, err := c.RemovePodSandbox(context.Background(), &runtime.RemovePodSandboxRequest{ | ||||
| 			PodSandboxId: testID, | ||||
| 		}) | ||||
| 		assert.Equal(t, test.expectCalls, fakeExecutionClient.GetCalledNames()) | ||||
| 		if test.expectErr { | ||||
| 			assert.Error(t, err) | ||||
| 			assert.Nil(t, res) | ||||
| 			continue | ||||
| 		} | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.NotNil(t, res) | ||||
| 		assert.NoError(t, c.sandboxNameIndex.Reserve(testName, testID), | ||||
| 			"sandbox name should be released") | ||||
| 		_, err = c.sandboxStore.Get(testID) | ||||
| 		assert.Equal(t, store.ErrNotExist, err, "sandbox should be removed") | ||||
| 		mountsResp, err := fakeSnapshotClient.Mounts(context.Background(), &snapshotapi.MountsRequest{Key: testID}) | ||||
| 		assert.Equal(t, servertesting.SnapshotNotExistError, err, "snapshot should be removed") | ||||
| 		assert.Nil(t, mountsResp) | ||||
| 		getResp, err := fake.Get(context.Background(), &containers.GetContainerRequest{ID: testID}) | ||||
| 		assert.Equal(t, servertesting.ContainerNotExistError, err, "containerd container should be removed") | ||||
| 		assert.Nil(t, getResp) | ||||
| 		res, err = c.RemovePodSandbox(context.Background(), &runtime.RemovePodSandboxRequest{ | ||||
| 			PodSandboxId: testID, | ||||
| 		}) | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.NotNil(t, res, "remove should be idempotent") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestRemoveContainersInSandbox(t *testing.T) { | ||||
| 	testID := "test-id" | ||||
| 	testName := "test-name" | ||||
| 	testSandbox := sandboxstore.Sandbox{ | ||||
| 		Metadata: sandboxstore.Metadata{ | ||||
| 			ID:   testID, | ||||
| 			Name: testName, | ||||
| 		}, | ||||
| 	} | ||||
| 	testContainers := []containerForTest{ | ||||
| 		{ | ||||
| 			metadata: containerstore.Metadata{ | ||||
| 				ID:        "test-cid-1", | ||||
| 				Name:      "test-cname-1", | ||||
| 				SandboxID: testID, | ||||
| 			}, | ||||
| 			status: containerstore.Status{FinishedAt: time.Now().UnixNano()}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			metadata: containerstore.Metadata{ | ||||
|  | ||||
| 				ID:        "test-cid-2", | ||||
| 				Name:      "test-cname-2", | ||||
| 				SandboxID: testID, | ||||
| 			}, | ||||
| 			status: containerstore.Status{FinishedAt: time.Now().UnixNano()}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			metadata: containerstore.Metadata{ | ||||
| 				ID:        "test-cid-3", | ||||
| 				Name:      "test-cname-3", | ||||
| 				SandboxID: "other-sandbox-id", | ||||
| 			}, | ||||
| 			status: containerstore.Status{FinishedAt: time.Now().UnixNano()}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	c := newTestCRIContainerdService() | ||||
| 	WithFakeSnapshotClient(c) | ||||
| 	assert.NoError(t, c.sandboxNameIndex.Reserve(testName, testID)) | ||||
| 	assert.NoError(t, c.sandboxStore.Add(testSandbox)) | ||||
| 	for _, tc := range testContainers { | ||||
| 		assert.NoError(t, c.containerNameIndex.Reserve(tc.metadata.Name, tc.metadata.ID)) | ||||
| 		cntr, err := tc.toContainer() | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.NoError(t, c.containerStore.Add(cntr)) | ||||
| 	} | ||||
|  | ||||
| 	res, err := c.RemovePodSandbox(context.Background(), &runtime.RemovePodSandboxRequest{ | ||||
| 		PodSandboxId: testID, | ||||
| 	}) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.NotNil(t, res) | ||||
|  | ||||
| 	_, err = c.sandboxStore.Get(testID) | ||||
| 	assert.Equal(t, store.ErrNotExist, err, "sandbox metadata should be removed") | ||||
|  | ||||
| 	cntrs := c.containerStore.List() | ||||
| 	assert.Len(t, cntrs, 1) | ||||
| 	assert.Equal(t, testContainers[2].metadata, cntrs[0].Metadata, "container should be removed") | ||||
| 	assert.Equal(t, testContainers[2].status, cntrs[0].Status.Get(), "container should be removed") | ||||
| } | ||||
| @@ -17,25 +17,16 @@ limitations under the License. | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"syscall" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/containerd/containerd/api/services/containers" | ||||
| 	"github.com/containerd/containerd/api/services/execution" | ||||
| 	snapshotapi "github.com/containerd/containerd/api/services/snapshot" | ||||
| 	imagespec "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| 	runtimespec "github.com/opencontainers/runtime-spec/specs-go" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" | ||||
|  | ||||
| 	ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing" | ||||
| 	servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing" | ||||
| 	imagestore "github.com/kubernetes-incubator/cri-containerd/pkg/store/image" | ||||
| ) | ||||
|  | ||||
| func getRunPodSandboxTestData() (*runtime.PodSandboxConfig, *imagespec.ImageConfig, func(*testing.T, string, *runtimespec.Spec)) { | ||||
| @@ -269,112 +260,6 @@ options timeout:1 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestRunPodSandbox(t *testing.T) { | ||||
| 	config, imageConfig, specCheck := getRunPodSandboxTestData() | ||||
| 	c := newTestCRIContainerdService() | ||||
| 	fake := c.containerService.(*servertesting.FakeContainersClient) | ||||
| 	fakeSnapshotClient := WithFakeSnapshotClient(c) | ||||
| 	fakeExecutionClient := c.taskService.(*servertesting.FakeExecutionClient) | ||||
| 	fakeCNIPlugin := c.netPlugin.(*servertesting.FakeCNIPlugin) | ||||
| 	fakeOS := c.os.(*ostesting.FakeOS) | ||||
| 	var dirs []string | ||||
| 	var pipes []string | ||||
| 	fakeOS.MkdirAllFn = func(path string, perm os.FileMode) error { | ||||
| 		dirs = append(dirs, path) | ||||
| 		return nil | ||||
| 	} | ||||
| 	fakeOS.OpenFifoFn = func(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) { | ||||
| 		pipes = append(pipes, fn) | ||||
| 		assert.Equal(t, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, flag) | ||||
| 		assert.Equal(t, os.FileMode(0700), perm) | ||||
| 		return nopReadWriteCloser{}, nil | ||||
| 	} | ||||
| 	testChainID := "test-sandbox-chain-id" | ||||
| 	image := imagestore.Image{ | ||||
| 		ID:      testSandboxImage, | ||||
| 		ChainID: testChainID, | ||||
| 		Config:  imageConfig, | ||||
| 	} | ||||
| 	// Insert sandbox image. | ||||
| 	c.imageStore.Add(image) | ||||
| 	expectContainersClientCalls := []string{"create"} | ||||
| 	expectSnapshotClientCalls := []string{"view"} | ||||
| 	expectExecutionClientCalls := []string{"create", "start"} | ||||
|  | ||||
| 	res, err := c.RunPodSandbox(context.Background(), &runtime.RunPodSandboxRequest{Config: config}) | ||||
| 	assert.NoError(t, err) | ||||
| 	require.NotNil(t, res) | ||||
| 	id := res.GetPodSandboxId() | ||||
|  | ||||
| 	sandboxRootDir := getSandboxRootDir(c.rootDir, id) | ||||
| 	assert.Contains(t, dirs[0], sandboxRootDir, "sandbox root directory should be created") | ||||
|  | ||||
| 	assert.Len(t, pipes, 2) | ||||
| 	_, stdout, stderr := getStreamingPipes(sandboxRootDir) | ||||
| 	assert.Contains(t, pipes, stdout, "sandbox stdout pipe should be created") | ||||
| 	assert.Contains(t, pipes, stderr, "sandbox stderr pipe should be created") | ||||
|  | ||||
| 	assert.Equal(t, expectSnapshotClientCalls, fakeSnapshotClient.GetCalledNames(), "expect snapshot functions should be called") | ||||
| 	calls := fakeSnapshotClient.GetCalledDetails() | ||||
| 	prepareOpts := calls[0].Argument.(*snapshotapi.PrepareRequest) | ||||
| 	assert.Equal(t, &snapshotapi.PrepareRequest{ | ||||
| 		Key:    id, | ||||
| 		Parent: testChainID, | ||||
| 	}, prepareOpts, "prepare request should be correct") | ||||
|  | ||||
| 	assert.Equal(t, expectContainersClientCalls, fake.GetCalledNames(), "expect containers functions should be called") | ||||
| 	calls = fake.GetCalledDetails() | ||||
| 	createOpts, ok := calls[0].Argument.(*containers.CreateContainerRequest) | ||||
| 	assert.True(t, ok, "should create sandbox container") | ||||
| 	assert.Equal(t, id, createOpts.Container.ID, "container id should be correct") | ||||
| 	assert.Equal(t, testSandboxImage, createOpts.Container.Image, "test image should be correct") | ||||
| 	assert.Equal(t, id, createOpts.Container.RootFS, "rootfs should be correct") | ||||
| 	spec := &runtimespec.Spec{} | ||||
| 	assert.NoError(t, json.Unmarshal(createOpts.Container.Spec.Value, spec)) | ||||
| 	t.Logf("oci spec check") | ||||
| 	specCheck(t, id, spec) | ||||
|  | ||||
| 	assert.Equal(t, expectExecutionClientCalls, fakeExecutionClient.GetCalledNames(), "expect execution functions should be called") | ||||
| 	calls = fakeExecutionClient.GetCalledDetails() | ||||
| 	createTaskOpts := calls[0].Argument.(*execution.CreateRequest) | ||||
| 	mountsResp, err := fakeSnapshotClient.Mounts(context.Background(), &snapshotapi.MountsRequest{Key: id}) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, &execution.CreateRequest{ | ||||
| 		ContainerID: id, | ||||
| 		Rootfs:      mountsResp.Mounts, | ||||
| 		Stdout:      stdout, | ||||
| 		Stderr:      stderr, | ||||
| 	}, createTaskOpts, "create options should be correct") | ||||
|  | ||||
| 	startID := calls[1].Argument.(*execution.StartRequest).ContainerID | ||||
| 	assert.Equal(t, id, startID, "start id should be correct") | ||||
|  | ||||
| 	sandbox, err := c.sandboxStore.Get(id) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, id, sandbox.ID, "sandbox id should be correct") | ||||
| 	err = c.sandboxNameIndex.Reserve(sandbox.Name, "random-id") | ||||
| 	assert.Error(t, err, "sandbox name should be reserved") | ||||
| 	assert.Equal(t, config, sandbox.Config, "sandbox config should be correct") | ||||
| 	// TODO(random-liu): [P2] Add clock interface and use fake clock. | ||||
| 	assert.NotZero(t, sandbox.CreatedAt, "sandbox CreatedAt should be set") | ||||
| 	info, err := fakeExecutionClient.Info(context.Background(), &execution.InfoRequest{ContainerID: id}) | ||||
| 	assert.NoError(t, err) | ||||
| 	pid := info.Task.Pid | ||||
| 	assert.Equal(t, sandbox.NetNS, getNetworkNamespace(pid), "sandbox network namespace should be correct") | ||||
|  | ||||
| 	expectedCNICalls := []string{"SetUpPod"} | ||||
| 	assert.Equal(t, expectedCNICalls, fakeCNIPlugin.GetCalledNames(), "expect SetUpPod should be called") | ||||
| 	calls = fakeCNIPlugin.GetCalledDetails() | ||||
| 	pluginArgument := calls[0].Argument.(servertesting.CNIPluginArgument) | ||||
| 	expectedPluginArgument := servertesting.CNIPluginArgument{ | ||||
| 		NetnsPath:   sandbox.NetNS, | ||||
| 		Namespace:   config.GetMetadata().GetNamespace(), | ||||
| 		Name:        config.GetMetadata().GetName(), | ||||
| 		ContainerID: id, | ||||
| 	} | ||||
| 	assert.Equal(t, expectedPluginArgument, pluginArgument, "SetUpPod should be called with correct arguments") | ||||
| } | ||||
|  | ||||
| func TestParseDNSOption(t *testing.T) { | ||||
| 	for desc, test := range map[string]struct { | ||||
| 		servers         []string | ||||
|   | ||||
| @@ -1,218 +0,0 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd/api/types/task" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" | ||||
|  | ||||
| 	servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing" | ||||
| 	sandboxstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/sandbox" | ||||
| ) | ||||
|  | ||||
| // Variables used in the following test. | ||||
|  | ||||
| const ( | ||||
| 	sandboxStatusTestID    = "test-id" | ||||
| 	sandboxStatusTestIP    = "10.10.10.10" | ||||
| 	sandboxStatusTestNetNS = "test-netns" | ||||
| ) | ||||
|  | ||||
| func getSandboxStatusTestData() (*sandboxstore.Sandbox, *runtime.PodSandboxStatus) { | ||||
| 	config := &runtime.PodSandboxConfig{ | ||||
| 		Metadata: &runtime.PodSandboxMetadata{ | ||||
| 			Name:      "test-name", | ||||
| 			Uid:       "test-uid", | ||||
| 			Namespace: "test-ns", | ||||
| 			Attempt:   1, | ||||
| 		}, | ||||
| 		Linux: &runtime.LinuxPodSandboxConfig{ | ||||
| 			SecurityContext: &runtime.LinuxSandboxSecurityContext{ | ||||
| 				NamespaceOptions: &runtime.NamespaceOption{ | ||||
| 					HostNetwork: true, | ||||
| 					HostPid:     false, | ||||
| 					HostIpc:     true, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		Labels:      map[string]string{"a": "b"}, | ||||
| 		Annotations: map[string]string{"c": "d"}, | ||||
| 	} | ||||
|  | ||||
| 	createdAt := time.Now().UnixNano() | ||||
|  | ||||
| 	sandbox := &sandboxstore.Sandbox{ | ||||
| 		Metadata: sandboxstore.Metadata{ | ||||
| 			ID:        sandboxStatusTestID, | ||||
| 			Name:      "test-name", | ||||
| 			Config:    config, | ||||
| 			CreatedAt: createdAt, | ||||
| 			NetNS:     sandboxStatusTestNetNS, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	expectedStatus := &runtime.PodSandboxStatus{ | ||||
| 		Id:        sandboxStatusTestID, | ||||
| 		Metadata:  config.GetMetadata(), | ||||
| 		CreatedAt: createdAt, | ||||
| 		Network:   &runtime.PodSandboxNetworkStatus{Ip: ""}, | ||||
| 		Linux: &runtime.LinuxPodSandboxStatus{ | ||||
| 			Namespaces: &runtime.Namespace{ | ||||
| 				Options: &runtime.NamespaceOption{ | ||||
| 					HostNetwork: true, | ||||
| 					HostPid:     false, | ||||
| 					HostIpc:     true, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		Labels:      config.GetLabels(), | ||||
| 		Annotations: config.GetAnnotations(), | ||||
| 	} | ||||
|  | ||||
| 	return sandbox, expectedStatus | ||||
| } | ||||
|  | ||||
| func TestPodSandboxStatus(t *testing.T) { | ||||
| 	for desc, test := range map[string]struct { | ||||
| 		sandboxTasks     []task.Task | ||||
| 		injectSandbox    bool | ||||
| 		injectErr        error | ||||
| 		injectIP         bool | ||||
| 		injectCNIErr     error | ||||
| 		expectState      runtime.PodSandboxState | ||||
| 		expectErr        bool | ||||
| 		expectCalls      []string | ||||
| 		expectedCNICalls []string | ||||
| 	}{ | ||||
| 		"sandbox status without metadata": { | ||||
| 			injectSandbox:    false, | ||||
| 			expectErr:        true, | ||||
| 			expectCalls:      []string{}, | ||||
| 			expectedCNICalls: []string{}, | ||||
| 		}, | ||||
| 		"sandbox status with running sandbox container": { | ||||
| 			sandboxTasks: []task.Task{{ | ||||
| 				ID:     sandboxStatusTestID, | ||||
| 				Pid:    1, | ||||
| 				Status: task.StatusRunning, | ||||
| 			}}, | ||||
| 			injectSandbox:    true, | ||||
| 			expectState:      runtime.PodSandboxState_SANDBOX_READY, | ||||
| 			expectCalls:      []string{"info"}, | ||||
| 			expectedCNICalls: []string{"GetContainerNetworkStatus"}, | ||||
| 		}, | ||||
| 		"sandbox status with stopped sandbox container": { | ||||
| 			sandboxTasks: []task.Task{{ | ||||
| 				ID:     sandboxStatusTestID, | ||||
| 				Pid:    1, | ||||
| 				Status: task.StatusStopped, | ||||
| 			}}, | ||||
| 			injectSandbox:    true, | ||||
| 			expectState:      runtime.PodSandboxState_SANDBOX_NOTREADY, | ||||
| 			expectCalls:      []string{"info"}, | ||||
| 			expectedCNICalls: []string{"GetContainerNetworkStatus"}, | ||||
| 		}, | ||||
| 		"sandbox status with non-existing sandbox container": { | ||||
| 			sandboxTasks:     []task.Task{}, | ||||
| 			injectSandbox:    true, | ||||
| 			expectState:      runtime.PodSandboxState_SANDBOX_NOTREADY, | ||||
| 			expectCalls:      []string{"info"}, | ||||
| 			expectedCNICalls: []string{"GetContainerNetworkStatus"}, | ||||
| 		}, | ||||
| 		"sandbox status with arbitrary error": { | ||||
| 			sandboxTasks: []task.Task{{ | ||||
| 				ID:     sandboxStatusTestID, | ||||
| 				Pid:    1, | ||||
| 				Status: task.StatusRunning, | ||||
| 			}}, | ||||
| 			injectSandbox:    true, | ||||
| 			expectState:      runtime.PodSandboxState_SANDBOX_READY, | ||||
| 			injectErr:        errors.New("arbitrary error"), | ||||
| 			expectErr:        true, | ||||
| 			expectCalls:      []string{"info"}, | ||||
| 			expectedCNICalls: []string{}, | ||||
| 		}, | ||||
| 		"sandbox status with IP address": { | ||||
| 			sandboxTasks: []task.Task{{ | ||||
| 				ID:     sandboxStatusTestID, | ||||
| 				Pid:    1, | ||||
| 				Status: task.StatusRunning, | ||||
| 			}}, | ||||
| 			injectSandbox:    true, | ||||
| 			expectState:      runtime.PodSandboxState_SANDBOX_READY, | ||||
| 			expectCalls:      []string{"info"}, | ||||
| 			injectIP:         true, | ||||
| 			expectedCNICalls: []string{"GetContainerNetworkStatus"}, | ||||
| 		}, | ||||
| 		"sandbox status with GetContainerNetworkStatus returns error": { | ||||
| 			sandboxTasks: []task.Task{{ | ||||
| 				ID:     sandboxStatusTestID, | ||||
| 				Pid:    1, | ||||
| 				Status: task.StatusRunning, | ||||
| 			}}, | ||||
| 			injectSandbox:    true, | ||||
| 			expectState:      runtime.PodSandboxState_SANDBOX_READY, | ||||
| 			expectCalls:      []string{"info"}, | ||||
| 			expectedCNICalls: []string{"GetContainerNetworkStatus"}, | ||||
| 			injectCNIErr:     errors.New("get container network status error"), | ||||
| 		}, | ||||
| 	} { | ||||
| 		t.Logf("TestCase %q", desc) | ||||
| 		sandbox, expect := getSandboxStatusTestData() | ||||
| 		expect.Network.Ip = "" | ||||
| 		c := newTestCRIContainerdService() | ||||
| 		fake := c.taskService.(*servertesting.FakeExecutionClient) | ||||
| 		fakeCNIPlugin := c.netPlugin.(*servertesting.FakeCNIPlugin) | ||||
| 		fake.SetFakeTasks(test.sandboxTasks) | ||||
| 		if test.injectSandbox { | ||||
| 			assert.NoError(t, c.sandboxStore.Add(*sandbox)) | ||||
| 		} | ||||
| 		if test.injectErr != nil { | ||||
| 			fake.InjectError("info", test.injectErr) | ||||
| 		} | ||||
| 		if test.injectCNIErr != nil { | ||||
| 			fakeCNIPlugin.InjectError("GetContainerNetworkStatus", test.injectCNIErr) | ||||
| 		} | ||||
| 		if test.injectIP { | ||||
| 			fakeCNIPlugin.SetFakePodNetwork(sandbox.NetNS, sandbox.Config.GetMetadata().GetNamespace(), | ||||
| 				sandbox.Config.GetMetadata().GetName(), sandboxStatusTestID, sandboxStatusTestIP) | ||||
| 			expect.Network.Ip = sandboxStatusTestIP | ||||
| 		} | ||||
| 		res, err := c.PodSandboxStatus(context.Background(), &runtime.PodSandboxStatusRequest{ | ||||
| 			PodSandboxId: sandboxStatusTestID, | ||||
| 		}) | ||||
| 		assert.Equal(t, test.expectCalls, fake.GetCalledNames()) | ||||
| 		assert.Equal(t, test.expectedCNICalls, fakeCNIPlugin.GetCalledNames()) | ||||
| 		if test.expectErr { | ||||
| 			assert.Error(t, err) | ||||
| 			assert.Nil(t, res) | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		assert.NoError(t, err) | ||||
| 		require.NotNil(t, res) | ||||
| 		expect.State = test.expectState | ||||
| 		assert.Equal(t, expect, res.GetStatus()) | ||||
| 	} | ||||
| } | ||||
| @@ -1,292 +0,0 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"os" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd/api/services/execution" | ||||
| 	"github.com/containerd/containerd/api/types/task" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"google.golang.org/grpc" | ||||
| 	"google.golang.org/grpc/codes" | ||||
| 	"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" | ||||
|  | ||||
| 	ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing" | ||||
| 	servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing" | ||||
| 	containerstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/container" | ||||
| 	sandboxstore "github.com/kubernetes-incubator/cri-containerd/pkg/store/sandbox" | ||||
| ) | ||||
|  | ||||
| func TestStopPodSandbox(t *testing.T) { | ||||
| 	testID := "test-id" | ||||
| 	testSandbox := sandboxstore.Sandbox{ | ||||
| 		Metadata: sandboxstore.Metadata{ | ||||
| 			ID:   testID, | ||||
| 			Name: "test-name", | ||||
| 			Config: &runtime.PodSandboxConfig{ | ||||
| 				Metadata: &runtime.PodSandboxMetadata{ | ||||
| 					Name:      "test-name", | ||||
| 					Uid:       "test-uid", | ||||
| 					Namespace: "test-ns", | ||||
| 				}}, | ||||
| 			NetNS: "test-netns", | ||||
| 		}, | ||||
| 	} | ||||
| 	testContainer := task.Task{ | ||||
| 		ID:     testID, | ||||
| 		Pid:    1, | ||||
| 		Status: task.StatusRunning, | ||||
| 	} | ||||
|  | ||||
| 	for desc, test := range map[string]struct { | ||||
| 		sandboxTasks     []task.Task | ||||
| 		injectSandbox    bool | ||||
| 		injectErr        error | ||||
| 		injectStatErr    error | ||||
| 		injectCNIErr     error | ||||
| 		injectUnmountErr error | ||||
| 		expectErr        bool | ||||
| 		expectCalls      []string | ||||
| 		expectedCNICalls []string | ||||
| 	}{ | ||||
| 		"stop non-existing sandbox": { | ||||
| 			injectSandbox:    false, | ||||
| 			expectErr:        true, | ||||
| 			expectCalls:      []string{}, | ||||
| 			expectedCNICalls: []string{}, | ||||
| 		}, | ||||
| 		"stop sandbox with sandbox container": { | ||||
| 			sandboxTasks:     []task.Task{testContainer}, | ||||
| 			injectSandbox:    true, | ||||
| 			expectErr:        false, | ||||
| 			expectCalls:      []string{"delete"}, | ||||
| 			expectedCNICalls: []string{"TearDownPod"}, | ||||
| 		}, | ||||
| 		"stop sandbox with sandbox container not exist error": { | ||||
| 			sandboxTasks:  []task.Task{}, | ||||
| 			injectSandbox: true, | ||||
| 			// Inject error to make sure fake execution client returns error. | ||||
| 			injectErr:        servertesting.TaskNotExistError, | ||||
| 			expectErr:        false, | ||||
| 			expectCalls:      []string{"delete"}, | ||||
| 			expectedCNICalls: []string{"TearDownPod"}, | ||||
| 		}, | ||||
| 		"stop sandbox with with arbitrary error": { | ||||
| 			injectSandbox:    true, | ||||
| 			injectErr:        grpc.Errorf(codes.Unknown, "arbitrary error"), | ||||
| 			expectErr:        true, | ||||
| 			expectCalls:      []string{"delete"}, | ||||
| 			expectedCNICalls: []string{"TearDownPod"}, | ||||
| 		}, | ||||
| 		"stop sandbox with Stat returns arbitrary error": { | ||||
| 			sandboxTasks:     []task.Task{testContainer}, | ||||
| 			injectSandbox:    true, | ||||
| 			expectErr:        true, | ||||
| 			injectStatErr:    errors.New("arbitrary error"), | ||||
| 			expectCalls:      []string{}, | ||||
| 			expectedCNICalls: []string{}, | ||||
| 		}, | ||||
| 		"stop sandbox with Stat returns not exist error": { | ||||
| 			sandboxTasks:     []task.Task{testContainer}, | ||||
| 			injectSandbox:    true, | ||||
| 			expectErr:        false, | ||||
| 			expectCalls:      []string{"delete"}, | ||||
| 			injectStatErr:    os.ErrNotExist, | ||||
| 			expectedCNICalls: []string{}, | ||||
| 		}, | ||||
| 		"stop sandbox with TearDownPod fails": { | ||||
| 			sandboxTasks:     []task.Task{testContainer}, | ||||
| 			injectSandbox:    true, | ||||
| 			expectErr:        true, | ||||
| 			expectedCNICalls: []string{"TearDownPod"}, | ||||
| 			injectCNIErr:     errors.New("arbitrary error"), | ||||
| 			expectCalls:      []string{}, | ||||
| 		}, | ||||
| 		"stop sandbox with unmount error": { | ||||
| 			sandboxTasks:     []task.Task{testContainer}, | ||||
| 			injectSandbox:    true, | ||||
| 			expectErr:        true, | ||||
| 			expectedCNICalls: []string{"TearDownPod"}, | ||||
| 			injectCNIErr:     errors.New("arbitrary error"), | ||||
| 			injectUnmountErr: errors.New("arbitrary error"), | ||||
| 			expectCalls:      []string{}, | ||||
| 		}, | ||||
| 	} { | ||||
| 		t.Logf("TestCase %q", desc) | ||||
| 		c := newTestCRIContainerdService() | ||||
| 		fake := c.taskService.(*servertesting.FakeExecutionClient) | ||||
| 		fakeCNIPlugin := c.netPlugin.(*servertesting.FakeCNIPlugin) | ||||
| 		fakeOS := c.os.(*ostesting.FakeOS) | ||||
| 		fake.SetFakeTasks(test.sandboxTasks) | ||||
|  | ||||
| 		if test.injectSandbox { | ||||
| 			assert.NoError(t, c.sandboxStore.Add(testSandbox)) | ||||
| 		} | ||||
| 		if test.injectErr != nil { | ||||
| 			fake.InjectError("delete", test.injectErr) | ||||
| 		} | ||||
| 		if test.injectCNIErr != nil { | ||||
| 			fakeCNIPlugin.InjectError("TearDownPod", test.injectCNIErr) | ||||
| 		} | ||||
| 		if test.injectStatErr != nil { | ||||
| 			fakeOS.InjectError("Stat", test.injectStatErr) | ||||
| 		} | ||||
| 		if test.injectUnmountErr != nil { | ||||
| 			fakeOS.InjectError("Unmount", test.injectUnmountErr) | ||||
| 		} | ||||
| 		fakeCNIPlugin.SetFakePodNetwork(testSandbox.NetNS, testSandbox.Config.GetMetadata().GetNamespace(), | ||||
| 			testSandbox.Config.GetMetadata().GetName(), testID, sandboxStatusTestIP) | ||||
|  | ||||
| 		res, err := c.StopPodSandbox(context.Background(), &runtime.StopPodSandboxRequest{ | ||||
| 			PodSandboxId: testID, | ||||
| 		}) | ||||
| 		if test.expectErr { | ||||
| 			assert.Error(t, err) | ||||
| 			assert.Nil(t, res) | ||||
| 		} else { | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.NotNil(t, res) | ||||
| 		} | ||||
| 		assert.Equal(t, test.expectCalls, fake.GetCalledNames()) | ||||
| 		assert.Equal(t, test.expectedCNICalls, fakeCNIPlugin.GetCalledNames()) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestStopContainersInSandbox(t *testing.T) { | ||||
| 	testID := "test-id" | ||||
| 	testSandbox := sandboxstore.Sandbox{ | ||||
| 		Metadata: sandboxstore.Metadata{ | ||||
| 			ID:   testID, | ||||
| 			Name: "test-name", | ||||
| 			Config: &runtime.PodSandboxConfig{ | ||||
| 				Metadata: &runtime.PodSandboxMetadata{ | ||||
| 					Name:      "test-name", | ||||
| 					Uid:       "test-uid", | ||||
| 					Namespace: "test-ns", | ||||
| 				}}, | ||||
| 			NetNS: "test-netns", | ||||
| 		}, | ||||
| 	} | ||||
| 	testContainers := []containerForTest{ | ||||
| 		{ | ||||
| 			metadata: containerstore.Metadata{ | ||||
| 				ID:        "test-cid-1", | ||||
| 				Name:      "test-cname-1", | ||||
| 				SandboxID: testID, | ||||
| 			}, | ||||
| 			status: containerstore.Status{ | ||||
| 				Pid:       2, | ||||
| 				StartedAt: time.Now().UnixNano(), | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
|  | ||||
| 			metadata: containerstore.Metadata{ | ||||
| 				ID:        "test-cid-2", | ||||
| 				Name:      "test-cname-2", | ||||
| 				SandboxID: testID, | ||||
| 			}, | ||||
| 			status: containerstore.Status{ | ||||
| 				Pid:       3, | ||||
| 				StartedAt: time.Now().UnixNano(), | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			metadata: containerstore.Metadata{ | ||||
| 				ID:        "test-cid-3", | ||||
| 				Name:      "test-cname-3", | ||||
| 				SandboxID: "other-sandbox-id", | ||||
| 			}, | ||||
| 			status: containerstore.Status{ | ||||
| 				Pid:       4, | ||||
| 				StartedAt: time.Now().UnixNano(), | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	testContainerdContainers := []task.Task{ | ||||
| 		{ | ||||
| 			ID:     testID, | ||||
| 			Pid:    1, | ||||
| 			Status: task.StatusRunning, | ||||
| 		}, | ||||
| 		{ | ||||
| 			ID:     "test-cid-1", | ||||
| 			Pid:    2, | ||||
| 			Status: task.StatusRunning, | ||||
| 		}, | ||||
| 		{ | ||||
| 			ID:     "test-cid-2", | ||||
| 			Pid:    3, | ||||
| 			Status: task.StatusRunning, | ||||
| 		}, | ||||
| 		{ | ||||
| 			ID:     "test-cid-3", | ||||
| 			Pid:    4, | ||||
| 			Status: task.StatusRunning, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	c := newTestCRIContainerdService() | ||||
| 	fake := servertesting.NewFakeExecutionClient().WithEvents() | ||||
| 	defer fake.Stop() | ||||
| 	c.taskService = fake | ||||
| 	fake.SetFakeTasks(testContainerdContainers) | ||||
| 	c.sandboxStore.Add(testSandbox) | ||||
| 	for _, tc := range testContainers { | ||||
| 		cntr, err := tc.toContainer() | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.NoError(t, c.containerStore.Add(cntr)) | ||||
| 	} | ||||
|  | ||||
| 	fakeCNIPlugin := c.netPlugin.(*servertesting.FakeCNIPlugin) | ||||
| 	fakeCNIPlugin.SetFakePodNetwork(testSandbox.NetNS, testSandbox.Config.GetMetadata().GetNamespace(), | ||||
| 		testSandbox.Config.GetMetadata().GetName(), testID, sandboxStatusTestIP) | ||||
|  | ||||
| 	eventClient, err := fake.Events(context.Background(), &execution.EventsRequest{}) | ||||
| 	assert.NoError(t, err) | ||||
| 	// Start a simple test event monitor. | ||||
| 	go func(e execution.Tasks_EventsClient) { | ||||
| 		for { | ||||
| 			if err := c.handleEventStream(e); err != nil { // nolint: vetshadow | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	}(eventClient) | ||||
| 	res, err := c.StopPodSandbox(context.Background(), &runtime.StopPodSandboxRequest{ | ||||
| 		PodSandboxId: testID, | ||||
| 	}) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.NotNil(t, res) | ||||
|  | ||||
| 	cntrs := c.containerStore.List() | ||||
| 	assert.Len(t, cntrs, 3) | ||||
| 	expectedStates := map[string]runtime.ContainerState{ | ||||
| 		"test-cid-1": runtime.ContainerState_CONTAINER_EXITED, | ||||
| 		"test-cid-2": runtime.ContainerState_CONTAINER_EXITED, | ||||
| 		"test-cid-3": runtime.ContainerState_CONTAINER_RUNNING, | ||||
| 	} | ||||
| 	for id, expected := range expectedStates { | ||||
| 		cntr, err := c.containerStore.Get(id) | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.Equal(t, expected, cntr.Status.Get().State()) | ||||
| 	} | ||||
| } | ||||
| @@ -18,16 +18,6 @@ package server | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/containerd/containerd/api/services/execution" | ||||
| 	snapshotservice "github.com/containerd/containerd/services/snapshot" | ||||
| 	imagespec "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" | ||||
|  | ||||
| 	ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing" | ||||
| 	"github.com/kubernetes-incubator/cri-containerd/pkg/registrar" | ||||
| @@ -64,132 +54,7 @@ func newTestCRIContainerdService() *criContainerdService { | ||||
| 		sandboxNameIndex:   registrar.NewRegistrar(), | ||||
| 		containerStore:     containerstore.NewStore(), | ||||
| 		containerNameIndex: registrar.NewRegistrar(), | ||||
| 		taskService:        servertesting.NewFakeExecutionClient(), | ||||
| 		containerService:   servertesting.NewFakeContainersClient(), | ||||
| 		netPlugin:          servertesting.NewFakeCNIPlugin(), | ||||
| 		agentFactory:       agentstesting.NewFakeAgentFactory(), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithFakeSnapshotClient add and return fake snapshot client. | ||||
| func WithFakeSnapshotClient(c *criContainerdService) *servertesting.FakeSnapshotClient { | ||||
| 	fake := servertesting.NewFakeSnapshotClient() | ||||
| 	c.snapshotService = snapshotservice.NewSnapshotterFromClient(fake) | ||||
| 	return fake | ||||
| } | ||||
|  | ||||
| // Test all sandbox operations. | ||||
| func TestSandboxOperations(t *testing.T) { | ||||
| 	c := newTestCRIContainerdService() | ||||
| 	fake := c.taskService.(*servertesting.FakeExecutionClient) | ||||
| 	fakeOS := c.os.(*ostesting.FakeOS) | ||||
| 	fakeCNIPlugin := c.netPlugin.(*servertesting.FakeCNIPlugin) | ||||
| 	WithFakeSnapshotClient(c) | ||||
| 	fakeOS.OpenFifoFn = func(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) { | ||||
| 		return nopReadWriteCloser{}, nil | ||||
| 	} | ||||
| 	// Insert sandbox image metadata. | ||||
| 	c.imageStore.Add(imagestore.Image{ | ||||
| 		ID:      testSandboxImage, | ||||
| 		ChainID: "test-chain-id", | ||||
| 		Config:  &imagespec.ImageConfig{Entrypoint: []string{"/pause"}}, | ||||
| 	}) | ||||
|  | ||||
| 	config := &runtime.PodSandboxConfig{ | ||||
| 		Metadata: &runtime.PodSandboxMetadata{ | ||||
| 			Name:      "test-name", | ||||
| 			Uid:       "test-uid", | ||||
| 			Namespace: "test-ns", | ||||
| 			Attempt:   1, | ||||
| 		}, | ||||
| 		Hostname:     "test-hostname", | ||||
| 		LogDirectory: "test-log-directory", | ||||
| 		Labels:       map[string]string{"a": "b"}, | ||||
| 		Annotations:  map[string]string{"c": "d"}, | ||||
| 	} | ||||
|  | ||||
| 	t.Logf("should be able to run a pod sandbox") | ||||
| 	runRes, err := c.RunPodSandbox(context.Background(), &runtime.RunPodSandboxRequest{Config: config}) | ||||
| 	assert.NoError(t, err) | ||||
| 	require.NotNil(t, runRes) | ||||
| 	id := runRes.GetPodSandboxId() | ||||
|  | ||||
| 	t.Logf("should be able to get pod sandbox status") | ||||
| 	info, err := fake.Info(context.Background(), &execution.InfoRequest{ContainerID: id}) | ||||
| 	netns := getNetworkNamespace(info.Task.Pid) | ||||
| 	assert.NoError(t, err) | ||||
| 	expectSandboxStatus := &runtime.PodSandboxStatus{ | ||||
| 		Id:       id, | ||||
| 		Metadata: config.GetMetadata(), | ||||
| 		// TODO(random-liu): [P2] Use fake clock for CreatedAt. | ||||
| 		Network: &runtime.PodSandboxNetworkStatus{}, | ||||
| 		Linux: &runtime.LinuxPodSandboxStatus{ | ||||
| 			Namespaces: &runtime.Namespace{ | ||||
| 				Options: &runtime.NamespaceOption{ | ||||
| 					HostNetwork: false, | ||||
| 					HostPid:     false, | ||||
| 					HostIpc:     false, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		Labels:      config.GetLabels(), | ||||
| 		Annotations: config.GetAnnotations(), | ||||
| 	} | ||||
| 	statusRes, err := c.PodSandboxStatus(context.Background(), &runtime.PodSandboxStatusRequest{PodSandboxId: id}) | ||||
| 	assert.NoError(t, err) | ||||
| 	require.NotNil(t, statusRes) | ||||
| 	status := statusRes.GetStatus() | ||||
| 	expectSandboxStatus.CreatedAt = status.GetCreatedAt() | ||||
| 	ip, err := fakeCNIPlugin.GetContainerNetworkStatus(netns, config.GetMetadata().GetNamespace(), config.GetMetadata().GetName(), id) | ||||
| 	assert.NoError(t, err) | ||||
| 	expectSandboxStatus.Network.Ip = ip | ||||
| 	assert.Equal(t, expectSandboxStatus, status) | ||||
|  | ||||
| 	t.Logf("should be able to list pod sandboxes") | ||||
| 	expectSandbox := &runtime.PodSandbox{ | ||||
| 		Id:          id, | ||||
| 		Metadata:    config.GetMetadata(), | ||||
| 		State:       runtime.PodSandboxState_SANDBOX_NOTREADY, // TODO(mikebrow) converting to client... should this be ready? | ||||
| 		Labels:      config.GetLabels(), | ||||
| 		Annotations: config.GetAnnotations(), | ||||
| 	} | ||||
| 	listRes, err := c.ListPodSandbox(context.Background(), &runtime.ListPodSandboxRequest{}) | ||||
| 	assert.NoError(t, err) | ||||
| 	require.NotNil(t, listRes) | ||||
| 	sandboxes := listRes.GetItems() | ||||
| 	assert.Len(t, sandboxes, 1) | ||||
| 	expectSandbox.CreatedAt = sandboxes[0].CreatedAt | ||||
| 	assert.Equal(t, expectSandbox, sandboxes[0]) | ||||
|  | ||||
| 	t.Logf("should be able to stop a pod sandbox") | ||||
| 	stopRes, err := c.StopPodSandbox(context.Background(), &runtime.StopPodSandboxRequest{PodSandboxId: id}) | ||||
| 	assert.NoError(t, err) | ||||
| 	require.NotNil(t, stopRes) | ||||
| 	statusRes, err = c.PodSandboxStatus(context.Background(), &runtime.PodSandboxStatusRequest{PodSandboxId: id}) | ||||
| 	assert.NoError(t, err) | ||||
| 	require.NotNil(t, statusRes) | ||||
| 	assert.Equal(t, runtime.PodSandboxState_SANDBOX_NOTREADY, statusRes.GetStatus().GetState(), | ||||
| 		"sandbox status should be NOTREADY after stopped") | ||||
| 	listRes, err = c.ListPodSandbox(context.Background(), &runtime.ListPodSandboxRequest{}) | ||||
| 	assert.NoError(t, err) | ||||
| 	require.NotNil(t, listRes) | ||||
| 	assert.Len(t, listRes.GetItems(), 1) | ||||
| 	assert.Equal(t, runtime.PodSandboxState_SANDBOX_NOTREADY, listRes.GetItems()[0].State, | ||||
| 		"sandbox in list should be NOTREADY after stopped") | ||||
|  | ||||
| 	t.Logf("should be able to remove a pod sandbox") | ||||
| 	removeRes, err := c.RemovePodSandbox(context.Background(), &runtime.RemovePodSandboxRequest{PodSandboxId: id}) | ||||
| 	assert.NoError(t, err) | ||||
| 	require.NotNil(t, removeRes) | ||||
| 	_, err = c.PodSandboxStatus(context.Background(), &runtime.PodSandboxStatusRequest{PodSandboxId: id}) | ||||
| 	assert.Error(t, err, "should not be able to get sandbox status after removed") | ||||
| 	listRes, err = c.ListPodSandbox(context.Background(), &runtime.ListPodSandboxRequest{}) | ||||
| 	assert.NoError(t, err) | ||||
| 	require.NotNil(t, listRes) | ||||
| 	assert.Empty(t, listRes.GetItems(), "should not be able to list the sandbox after removed") | ||||
|  | ||||
| 	t.Logf("should be able to create the sandbox again") | ||||
| 	runRes, err = c.RunPodSandbox(context.Background(), &runtime.RunPodSandboxRequest{Config: config}) | ||||
| 	assert.NoError(t, err) | ||||
| 	require.NotNil(t, runRes) | ||||
| } | ||||
|   | ||||
| @@ -26,6 +26,14 @@ import ( | ||||
| 	"github.com/kubernetes-incubator/cri-o/pkg/ocicni" | ||||
| ) | ||||
|  | ||||
| // CalledDetail is the struct contains called function name and arguments. | ||||
| type CalledDetail struct { | ||||
| 	// Name of the function called. | ||||
| 	Name string | ||||
| 	// Argument of the function called. | ||||
| 	Argument interface{} | ||||
| } | ||||
|  | ||||
| // CNIPluginArgument is arguments used to call CNI related functions. | ||||
| type CNIPluginArgument struct { | ||||
| 	NetnsPath   string | ||||
|   | ||||
| @@ -1,181 +0,0 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package testing | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/containerd/containerd/api/services/containers" | ||||
| 	googleprotobuf "github.com/golang/protobuf/ptypes/empty" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"google.golang.org/grpc" | ||||
| 	"google.golang.org/grpc/codes" | ||||
| ) | ||||
|  | ||||
| // ContainerNotExistError is the fake error returned when container does not exist. | ||||
| var ContainerNotExistError = grpc.Errorf(codes.NotFound, "container does not exist") | ||||
|  | ||||
| // FakeContainersClient is a simple fake containers client, so that cri-containerd | ||||
| // can be run for testing without requiring a real containerd setup. | ||||
| type FakeContainersClient struct { | ||||
| 	sync.Mutex | ||||
| 	called        []CalledDetail | ||||
| 	errors        map[string]error | ||||
| 	ContainerList map[string]containers.Container | ||||
| } | ||||
|  | ||||
| var _ containers.ContainersClient = &FakeContainersClient{} | ||||
|  | ||||
| // NewFakeContainersClient creates a FakeContainersClient | ||||
| func NewFakeContainersClient() *FakeContainersClient { | ||||
| 	return &FakeContainersClient{ | ||||
| 		errors:        make(map[string]error), | ||||
| 		ContainerList: make(map[string]containers.Container), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (f *FakeContainersClient) getError(op string) error { | ||||
| 	err, ok := f.errors[op] | ||||
| 	if ok { | ||||
| 		delete(f.errors, op) | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // InjectError inject error for call | ||||
| func (f *FakeContainersClient) InjectError(fn string, err error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.errors[fn] = err | ||||
| } | ||||
|  | ||||
| // InjectErrors inject errors for calls | ||||
| func (f *FakeContainersClient) InjectErrors(errs map[string]error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	for fn, err := range errs { | ||||
| 		f.errors[fn] = err | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // ClearErrors clear errors for call | ||||
| func (f *FakeContainersClient) ClearErrors() { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.errors = make(map[string]error) | ||||
| } | ||||
|  | ||||
| func (f *FakeContainersClient) appendCalled(name string, argument interface{}) { | ||||
| 	call := CalledDetail{Name: name, Argument: argument} | ||||
| 	f.called = append(f.called, call) | ||||
| } | ||||
|  | ||||
| // GetCalledNames get names of call | ||||
| func (f *FakeContainersClient) GetCalledNames() []string { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	names := []string{} | ||||
| 	for _, detail := range f.called { | ||||
| 		names = append(names, detail.Name) | ||||
| 	} | ||||
| 	return names | ||||
| } | ||||
|  | ||||
| // ClearCalls clear all call detail. | ||||
| func (f *FakeContainersClient) ClearCalls() { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.called = []CalledDetail{} | ||||
| } | ||||
|  | ||||
| // GetCalledDetails get detail of each call. | ||||
| func (f *FakeContainersClient) GetCalledDetails() []CalledDetail { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	// Copy the list and return. | ||||
| 	return append([]CalledDetail{}, f.called...) | ||||
| } | ||||
|  | ||||
| // Create is a test implementation of containers.Create. | ||||
| func (f *FakeContainersClient) Create(ctx context.Context, createOpts *containers.CreateContainerRequest, opts ...grpc.CallOption) (*containers.CreateContainerResponse, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("create", createOpts) | ||||
| 	if err := f.getError("create"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	_, ok := f.ContainerList[createOpts.Container.ID] | ||||
| 	if ok { | ||||
| 		return nil, fmt.Errorf("container already exists") | ||||
| 	} | ||||
| 	f.ContainerList[createOpts.Container.ID] = createOpts.Container | ||||
| 	return &containers.CreateContainerResponse{Container: createOpts.Container}, nil | ||||
| } | ||||
|  | ||||
| // Delete is a test implementation of containers.Delete | ||||
| func (f *FakeContainersClient) Delete(ctx context.Context, deleteOpts *containers.DeleteContainerRequest, opts ...grpc.CallOption) (*googleprotobuf.Empty, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("delete", deleteOpts) | ||||
| 	if err := f.getError("delete"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	_, ok := f.ContainerList[deleteOpts.ID] | ||||
| 	if !ok { | ||||
| 		return nil, ContainerNotExistError | ||||
| 	} | ||||
| 	delete(f.ContainerList, deleteOpts.ID) | ||||
| 	return &googleprotobuf.Empty{}, nil | ||||
| } | ||||
|  | ||||
| // Get is a test implementation of containers.Get | ||||
| func (f *FakeContainersClient) Get(ctx context.Context, getOpts *containers.GetContainerRequest, opts ...grpc.CallOption) (*containers.GetContainerResponse, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("get", getOpts) | ||||
| 	if err := f.getError("get"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	c, ok := f.ContainerList[getOpts.ID] | ||||
| 	if !ok { | ||||
| 		return nil, ContainerNotExistError | ||||
| 	} | ||||
| 	return &containers.GetContainerResponse{Container: c}, nil | ||||
| } | ||||
|  | ||||
| // List is a test implementation of containers.List | ||||
| func (f *FakeContainersClient) List(ctx context.Context, listOpts *containers.ListContainersRequest, opts ...grpc.CallOption) (*containers.ListContainersResponse, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("list", listOpts) | ||||
| 	if err := f.getError("list"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	var cs []containers.Container | ||||
| 	for _, c := range f.ContainerList { | ||||
| 		cs = append(cs, c) | ||||
| 	} | ||||
| 	return &containers.ListContainersResponse{Containers: cs}, nil | ||||
| } | ||||
|  | ||||
| // Update is a test implementation of containers.Update | ||||
| func (f *FakeContainersClient) Update(ctx context.Context, updateOpts *containers.UpdateContainerRequest, opts ...grpc.CallOption) (*containers.UpdateContainerResponse, error) { | ||||
| 	// TODO: implement Update() | ||||
| 	return nil, nil | ||||
| } | ||||
| @@ -1,392 +0,0 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package testing | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"math/rand" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd/api/services/execution" | ||||
| 	"github.com/containerd/containerd/api/types/task" | ||||
| 	googleprotobuf "github.com/golang/protobuf/ptypes/empty" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"google.golang.org/grpc" | ||||
| 	"google.golang.org/grpc/codes" | ||||
| ) | ||||
|  | ||||
| // TaskNotExistError is the fake error returned when task does not exist. | ||||
| var TaskNotExistError = grpc.Errorf(codes.NotFound, "task does not exist") | ||||
|  | ||||
| // CalledDetail is the struct contains called function name and arguments. | ||||
| type CalledDetail struct { | ||||
| 	// Name of the function called. | ||||
| 	Name string | ||||
| 	// Argument of the function called. | ||||
| 	Argument interface{} | ||||
| } | ||||
|  | ||||
| var _ execution.Tasks_EventsClient = &EventClient{} | ||||
|  | ||||
| // EventClient is a test implementation of execution.Tasks_EventsClient | ||||
| type EventClient struct { | ||||
| 	Events chan *task.Event | ||||
| 	grpc.ClientStream | ||||
| } | ||||
|  | ||||
| // Recv is a test implementation of Recv | ||||
| func (cli *EventClient) Recv() (*task.Event, error) { | ||||
| 	event, ok := <-cli.Events | ||||
| 	if !ok { | ||||
| 		return nil, fmt.Errorf("event channel closed") | ||||
| 	} | ||||
| 	return event, nil | ||||
| } | ||||
|  | ||||
| // FakeExecutionClient is a simple fake execution client, so that cri-containerd | ||||
| // can be run for testing without requiring a real containerd setup. | ||||
| type FakeExecutionClient struct { | ||||
| 	sync.Mutex | ||||
| 	called       []CalledDetail | ||||
| 	errors       map[string]error | ||||
| 	TaskList     map[string]task.Task | ||||
| 	eventsQueue  chan *task.Event | ||||
| 	eventClients []*EventClient | ||||
| } | ||||
|  | ||||
| var _ execution.TasksClient = &FakeExecutionClient{} | ||||
|  | ||||
| // NewFakeExecutionClient creates a FakeExecutionClient | ||||
| func NewFakeExecutionClient() *FakeExecutionClient { | ||||
| 	return &FakeExecutionClient{ | ||||
| 		errors:   make(map[string]error), | ||||
| 		TaskList: make(map[string]task.Task), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Stop the fake execution service. Needed when event is enabled. | ||||
| func (f *FakeExecutionClient) Stop() { | ||||
| 	if f.eventsQueue != nil { | ||||
| 		close(f.eventsQueue) | ||||
| 	} | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	for _, client := range f.eventClients { | ||||
| 		close(client.Events) | ||||
| 	} | ||||
| 	f.eventClients = nil | ||||
| } | ||||
|  | ||||
| // WithEvents setup events publisher for FakeExecutionClient | ||||
| func (f *FakeExecutionClient) WithEvents() *FakeExecutionClient { | ||||
| 	f.eventsQueue = make(chan *task.Event, 1024) | ||||
| 	go func() { | ||||
| 		for e := range f.eventsQueue { | ||||
| 			f.Lock() | ||||
| 			for _, client := range f.eventClients { | ||||
| 				client.Events <- e | ||||
| 			} | ||||
| 			f.Unlock() | ||||
| 		} | ||||
| 	}() | ||||
| 	return f | ||||
| } | ||||
|  | ||||
| func (f *FakeExecutionClient) getError(op string) error { | ||||
| 	err, ok := f.errors[op] | ||||
| 	if ok { | ||||
| 		delete(f.errors, op) | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // InjectError inject error for call | ||||
| func (f *FakeExecutionClient) InjectError(fn string, err error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.errors[fn] = err | ||||
| } | ||||
|  | ||||
| // InjectErrors inject errors for calls | ||||
| func (f *FakeExecutionClient) InjectErrors(errs map[string]error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	for fn, err := range errs { | ||||
| 		f.errors[fn] = err | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // ClearErrors clear errors for call | ||||
| func (f *FakeExecutionClient) ClearErrors() { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.errors = make(map[string]error) | ||||
| } | ||||
|  | ||||
| func generatePid() uint32 { | ||||
| 	rand.Seed(time.Now().Unix()) | ||||
| 	randPid := uint32(rand.Intn(1000)) | ||||
| 	return randPid | ||||
| } | ||||
|  | ||||
| func (f *FakeExecutionClient) sendEvent(event *task.Event) { | ||||
| 	if f.eventsQueue != nil { | ||||
| 		f.eventsQueue <- event | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (f *FakeExecutionClient) appendCalled(name string, argument interface{}) { | ||||
| 	call := CalledDetail{Name: name, Argument: argument} | ||||
| 	f.called = append(f.called, call) | ||||
| } | ||||
|  | ||||
| // GetCalledNames get names of call | ||||
| func (f *FakeExecutionClient) GetCalledNames() []string { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	names := []string{} | ||||
| 	for _, detail := range f.called { | ||||
| 		names = append(names, detail.Name) | ||||
| 	} | ||||
| 	return names | ||||
| } | ||||
|  | ||||
| // ClearCalls clear all call detail. | ||||
| func (f *FakeExecutionClient) ClearCalls() { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.called = []CalledDetail{} | ||||
| } | ||||
|  | ||||
| // GetCalledDetails get detail of each call. | ||||
| func (f *FakeExecutionClient) GetCalledDetails() []CalledDetail { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	// Copy the list and return. | ||||
| 	return append([]CalledDetail{}, f.called...) | ||||
| } | ||||
|  | ||||
| // SetFakeTasks injects fake tasks. | ||||
| func (f *FakeExecutionClient) SetFakeTasks(tasks []task.Task) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	for _, t := range tasks { | ||||
| 		f.TaskList[t.ID] = t | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Create is a test implementation of execution.Create. | ||||
| func (f *FakeExecutionClient) Create(ctx context.Context, createOpts *execution.CreateRequest, opts ...grpc.CallOption) (*execution.CreateResponse, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("create", createOpts) | ||||
| 	if err := f.getError("create"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	_, ok := f.TaskList[createOpts.ContainerID] | ||||
| 	if ok { | ||||
| 		return nil, fmt.Errorf("task already exists") | ||||
| 	} | ||||
| 	pid := generatePid() | ||||
| 	f.TaskList[createOpts.ContainerID] = task.Task{ | ||||
| 		ContainerID: createOpts.ContainerID, | ||||
| 		Pid:         pid, | ||||
| 		Status:      task.StatusCreated, | ||||
| 	} | ||||
| 	f.sendEvent(&task.Event{ | ||||
| 		ID:   createOpts.ContainerID, | ||||
| 		Type: task.Event_CREATE, | ||||
| 		Pid:  pid, | ||||
| 	}) | ||||
| 	return &execution.CreateResponse{ | ||||
| 		ContainerID: createOpts.ContainerID, | ||||
| 		Pid:         pid, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // Start is a test implementation of execution.Start | ||||
| func (f *FakeExecutionClient) Start(ctx context.Context, startOpts *execution.StartRequest, opts ...grpc.CallOption) (*googleprotobuf.Empty, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("start", startOpts) | ||||
| 	if err := f.getError("start"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	c, ok := f.TaskList[startOpts.ContainerID] | ||||
| 	if !ok { | ||||
| 		return nil, TaskNotExistError | ||||
| 	} | ||||
| 	f.sendEvent(&task.Event{ | ||||
| 		ID:   c.ID, | ||||
| 		Type: task.Event_START, | ||||
| 		Pid:  c.Pid, | ||||
| 	}) | ||||
| 	switch c.Status { | ||||
| 	case task.StatusCreated: | ||||
| 		c.Status = task.StatusRunning | ||||
| 		f.TaskList[startOpts.ContainerID] = c | ||||
| 		return &googleprotobuf.Empty{}, nil | ||||
| 	case task.StatusStopped: | ||||
| 		return &googleprotobuf.Empty{}, fmt.Errorf("cannot start a container that has stopped") | ||||
| 	case task.StatusRunning: | ||||
| 		return &googleprotobuf.Empty{}, fmt.Errorf("cannot start an already running container") | ||||
| 	default: | ||||
| 		return &googleprotobuf.Empty{}, fmt.Errorf("cannot start a container in the %s state", c.Status) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Delete is a test implementation of execution.Delete | ||||
| func (f *FakeExecutionClient) Delete(ctx context.Context, deleteOpts *execution.DeleteRequest, opts ...grpc.CallOption) (*execution.DeleteResponse, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("delete", deleteOpts) | ||||
| 	if err := f.getError("delete"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	c, ok := f.TaskList[deleteOpts.ContainerID] | ||||
| 	if !ok { | ||||
| 		return nil, TaskNotExistError | ||||
| 	} | ||||
| 	delete(f.TaskList, deleteOpts.ContainerID) | ||||
| 	f.sendEvent(&task.Event{ | ||||
| 		ID:   c.ID, | ||||
| 		Type: task.Event_EXIT, | ||||
| 		Pid:  c.Pid, | ||||
| 	}) | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| // Info is a test implementation of execution.Info | ||||
| func (f *FakeExecutionClient) Info(ctx context.Context, infoOpts *execution.InfoRequest, opts ...grpc.CallOption) (*execution.InfoResponse, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("info", infoOpts) | ||||
| 	if err := f.getError("info"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	c, ok := f.TaskList[infoOpts.ContainerID] | ||||
| 	if !ok { | ||||
| 		return nil, TaskNotExistError | ||||
| 	} | ||||
| 	return &execution.InfoResponse{Task: &c}, nil | ||||
| } | ||||
|  | ||||
| // List is a test implementation of execution.List | ||||
| func (f *FakeExecutionClient) List(ctx context.Context, listOpts *execution.ListRequest, opts ...grpc.CallOption) (*execution.ListResponse, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("list", listOpts) | ||||
| 	if err := f.getError("list"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	resp := &execution.ListResponse{} | ||||
| 	for _, c := range f.TaskList { | ||||
| 		resp.Tasks = append(resp.Tasks, &task.Task{ | ||||
| 			ID:     c.ID, | ||||
| 			Pid:    c.Pid, | ||||
| 			Status: c.Status, | ||||
| 		}) | ||||
| 	} | ||||
| 	return resp, nil | ||||
| } | ||||
|  | ||||
| // Kill is a test implementation of execution.Kill | ||||
| func (f *FakeExecutionClient) Kill(ctx context.Context, killOpts *execution.KillRequest, opts ...grpc.CallOption) (*googleprotobuf.Empty, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("kill", killOpts) | ||||
| 	if err := f.getError("kill"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	c, ok := f.TaskList[killOpts.ContainerID] | ||||
| 	if !ok { | ||||
| 		return nil, TaskNotExistError | ||||
| 	} | ||||
| 	c.Status = task.StatusStopped | ||||
| 	f.TaskList[killOpts.ContainerID] = c | ||||
| 	f.sendEvent(&task.Event{ | ||||
| 		ID:   c.ID, | ||||
| 		Type: task.Event_EXIT, | ||||
| 		Pid:  c.Pid, | ||||
| 	}) | ||||
| 	return &googleprotobuf.Empty{}, nil | ||||
| } | ||||
|  | ||||
| // Events is a test implementation of execution.Events | ||||
| func (f *FakeExecutionClient) Events(ctx context.Context, eventsOpts *execution.EventsRequest, opts ...grpc.CallOption) (execution.Tasks_EventsClient, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("events", eventsOpts) | ||||
| 	if err := f.getError("events"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	var client = &EventClient{ | ||||
| 		Events: make(chan *task.Event, 100), | ||||
| 	} | ||||
| 	f.eventClients = append(f.eventClients, client) | ||||
| 	return client, nil | ||||
| } | ||||
|  | ||||
| // Exec is a test implementation of execution.Exec | ||||
| func (f *FakeExecutionClient) Exec(ctx context.Context, execOpts *execution.ExecRequest, opts ...grpc.CallOption) (*execution.ExecResponse, error) { | ||||
| 	// TODO: implement Exec() | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| // Pty is a test implementation of execution.Pty | ||||
| func (f *FakeExecutionClient) Pty(ctx context.Context, ptyOpts *execution.PtyRequest, opts ...grpc.CallOption) (*googleprotobuf.Empty, error) { | ||||
| 	// TODO: implement Pty() | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| // CloseStdin is a test implementation of execution.CloseStdin | ||||
| func (f *FakeExecutionClient) CloseStdin(ctx context.Context, closeStdinOpts *execution.CloseStdinRequest, opts ...grpc.CallOption) (*googleprotobuf.Empty, error) { | ||||
| 	// TODO: implement CloseStdin() | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| // Pause is a test implementation of execution.Pause | ||||
| func (f *FakeExecutionClient) Pause(ctx context.Context, in *execution.PauseRequest, opts ...grpc.CallOption) (*googleprotobuf.Empty, error) { | ||||
| 	// TODO: implement Pause() | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| // Resume is a test implementation of execution.Resume | ||||
| func (f *FakeExecutionClient) Resume(ctx context.Context, in *execution.ResumeRequest, opts ...grpc.CallOption) (*googleprotobuf.Empty, error) { | ||||
| 	// TODO: implement Resume() | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| // Checkpoint is a test implementation of execution.Checkpoint | ||||
| func (f *FakeExecutionClient) Checkpoint(ctx context.Context, in *execution.CheckpointRequest, opts ...grpc.CallOption) (*execution.CheckpointResponse, error) { | ||||
| 	// TODO: implement Checkpoint() | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| // Processes is a test implementation of execution.Processes | ||||
| func (f *FakeExecutionClient) Processes(ctx context.Context, in *execution.ProcessesRequest, opts ...grpc.CallOption) (*execution.ProcessesResponse, error) { | ||||
| 	// TODO: implement Processes() | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| // DeleteProcess is a test implementation of execution.DeleteProcess | ||||
| func (f *FakeExecutionClient) DeleteProcess(ctx context.Context, in *execution.DeleteProcessRequest, opts ...grpc.CallOption) (*execution.DeleteResponse, error) { | ||||
| 	// TODO: implement DeleteProcess() | ||||
| 	return nil, nil | ||||
| } | ||||
| @@ -1,164 +0,0 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package testing | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/containerd/containerd/api/services/images" | ||||
| 	googleprotobuf "github.com/golang/protobuf/ptypes/empty" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"google.golang.org/grpc" | ||||
| ) | ||||
|  | ||||
| // FakeImagesClient is a simple fake images client, so that cri-containerd | ||||
| // can be run for testing without requiring a real containerd setup. | ||||
| type FakeImagesClient struct { | ||||
| 	sync.Mutex | ||||
| 	called    []CalledDetail | ||||
| 	errors    map[string]error | ||||
| 	ImageList map[string]images.Image | ||||
| } | ||||
|  | ||||
| var _ images.ImagesClient = &FakeImagesClient{} | ||||
|  | ||||
| // NewFakeImagesClient creates a FakeImagesClient | ||||
| func NewFakeImagesClient() *FakeImagesClient { | ||||
| 	return &FakeImagesClient{ | ||||
| 		errors:    make(map[string]error), | ||||
| 		ImageList: make(map[string]images.Image), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // getError get error for call | ||||
| func (f *FakeImagesClient) getError(op string) error { | ||||
| 	err, ok := f.errors[op] | ||||
| 	if ok { | ||||
| 		delete(f.errors, op) | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // InjectError inject error for call | ||||
| func (f *FakeImagesClient) InjectError(fn string, err error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.errors[fn] = err | ||||
| } | ||||
|  | ||||
| // InjectErrors inject errors for calls | ||||
| func (f *FakeImagesClient) InjectErrors(errs map[string]error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	for fn, err := range errs { | ||||
| 		f.errors[fn] = err | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // ClearErrors clear errors for call | ||||
| func (f *FakeImagesClient) ClearErrors() { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.errors = make(map[string]error) | ||||
| } | ||||
|  | ||||
| func (f *FakeImagesClient) appendCalled(name string, argument interface{}) { | ||||
| 	call := CalledDetail{Name: name, Argument: argument} | ||||
| 	f.called = append(f.called, call) | ||||
| } | ||||
|  | ||||
| // GetCalledNames get names of call | ||||
| func (f *FakeImagesClient) GetCalledNames() []string { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	names := []string{} | ||||
| 	for _, detail := range f.called { | ||||
| 		names = append(names, detail.Name) | ||||
| 	} | ||||
| 	return names | ||||
| } | ||||
|  | ||||
| // SetFakeImages injects fake images. | ||||
| func (f *FakeImagesClient) SetFakeImages(images []images.Image) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	for _, image := range images { | ||||
| 		f.ImageList[image.Name] = image | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Get is a test implementation of images.Get | ||||
| func (f *FakeImagesClient) Get(ctx context.Context, getOpts *images.GetRequest, opts ...grpc.CallOption) (*images.GetResponse, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("get", getOpts) | ||||
| 	if err := f.getError("get"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	image, ok := f.ImageList[getOpts.Name] | ||||
| 	if !ok { | ||||
| 		return nil, fmt.Errorf("image does not exist") | ||||
| 	} | ||||
| 	return &images.GetResponse{ | ||||
| 		Image: &image, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // Put is a test implementation of images.Put | ||||
| func (f *FakeImagesClient) Put(ctx context.Context, putOpts *images.PutRequest, opts ...grpc.CallOption) (*googleprotobuf.Empty, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("put", putOpts) | ||||
| 	if err := f.getError("put"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	f.ImageList[putOpts.Image.Name] = putOpts.Image | ||||
| 	return &googleprotobuf.Empty{}, nil | ||||
| } | ||||
|  | ||||
| // List is a test implementation of images.List | ||||
| func (f *FakeImagesClient) List(ctx context.Context, listOpts *images.ListRequest, opts ...grpc.CallOption) (*images.ListResponse, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("list", listOpts) | ||||
| 	if err := f.getError("list"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	resp := &images.ListResponse{} | ||||
| 	for _, image := range f.ImageList { | ||||
| 		resp.Images = append(resp.Images, image) | ||||
| 	} | ||||
| 	return resp, nil | ||||
| } | ||||
|  | ||||
| // Delete is a test implementation of images.Delete | ||||
| func (f *FakeImagesClient) Delete(ctx context.Context, deleteOpts *images.DeleteRequest, opts ...grpc.CallOption) (*googleprotobuf.Empty, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("delete", deleteOpts) | ||||
| 	if err := f.getError("delete"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	_, ok := f.ImageList[deleteOpts.Name] | ||||
| 	if !ok { | ||||
| 		return nil, fmt.Errorf("image does not exist") | ||||
| 	} | ||||
| 	delete(f.ImageList, deleteOpts.Name) | ||||
| 	return &googleprotobuf.Empty{}, nil | ||||
| } | ||||
| @@ -1,221 +0,0 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package testing | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/containerd/containerd/api/services/snapshot" | ||||
| 	"github.com/containerd/containerd/api/types/mount" | ||||
| 	google_protobuf1 "github.com/golang/protobuf/ptypes/empty" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"google.golang.org/grpc" | ||||
| 	"google.golang.org/grpc/codes" | ||||
| ) | ||||
|  | ||||
| // SnapshotNotExistError is the fake error returned when snapshot does not exist. | ||||
| var SnapshotNotExistError = grpc.Errorf(codes.NotFound, "snapshot does not exist") | ||||
|  | ||||
| // FakeSnapshotClient is a simple fake snapshot client, so that cri-containerd | ||||
| // can be run for testing without requiring a real containerd setup. | ||||
| type FakeSnapshotClient struct { | ||||
| 	sync.Mutex | ||||
| 	called    []CalledDetail | ||||
| 	errors    map[string]error | ||||
| 	MountList map[string][]*mount.Mount | ||||
| } | ||||
|  | ||||
| var _ snapshot.SnapshotClient = &FakeSnapshotClient{} | ||||
|  | ||||
| // NewFakeSnapshotClient creates a FakeSnapshotClient | ||||
| func NewFakeSnapshotClient() *FakeSnapshotClient { | ||||
| 	return &FakeSnapshotClient{ | ||||
| 		errors:    make(map[string]error), | ||||
| 		MountList: make(map[string][]*mount.Mount), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (f *FakeSnapshotClient) getError(op string) error { | ||||
| 	err, ok := f.errors[op] | ||||
| 	if ok { | ||||
| 		delete(f.errors, op) | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // InjectError inject error for call | ||||
| func (f *FakeSnapshotClient) InjectError(fn string, err error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.errors[fn] = err | ||||
| } | ||||
|  | ||||
| // InjectErrors inject errors for calls | ||||
| func (f *FakeSnapshotClient) InjectErrors(errs map[string]error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	for fn, err := range errs { | ||||
| 		f.errors[fn] = err | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // ClearErrors clear errors for call | ||||
| func (f *FakeSnapshotClient) ClearErrors() { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.errors = make(map[string]error) | ||||
| } | ||||
|  | ||||
| func (f *FakeSnapshotClient) appendCalled(name string, argument interface{}) { | ||||
| 	call := CalledDetail{Name: name, Argument: argument} | ||||
| 	f.called = append(f.called, call) | ||||
| } | ||||
|  | ||||
| // GetCalledNames get names of call | ||||
| func (f *FakeSnapshotClient) GetCalledNames() []string { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	names := []string{} | ||||
| 	for _, detail := range f.called { | ||||
| 		names = append(names, detail.Name) | ||||
| 	} | ||||
| 	return names | ||||
| } | ||||
|  | ||||
| // GetCalledDetails get detail of each call. | ||||
| func (f *FakeSnapshotClient) GetCalledDetails() []CalledDetail { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	// Copy the list and return. | ||||
| 	return append([]CalledDetail{}, f.called...) | ||||
| } | ||||
|  | ||||
| // SetFakeMounts injects fake mounts. | ||||
| func (f *FakeSnapshotClient) SetFakeMounts(name string, mounts []*mount.Mount) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.MountList[name] = mounts | ||||
| } | ||||
|  | ||||
| // ListMounts lists all the fake mounts. | ||||
| func (f *FakeSnapshotClient) ListMounts() [][]*mount.Mount { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	var ms [][]*mount.Mount | ||||
| 	for _, m := range f.MountList { | ||||
| 		ms = append(ms, m) | ||||
| 	} | ||||
| 	return ms | ||||
| } | ||||
|  | ||||
| // Prepare is a test implementation of snapshot.Prepare | ||||
| func (f *FakeSnapshotClient) Prepare(ctx context.Context, prepareOpts *snapshot.PrepareRequest, opts ...grpc.CallOption) (*snapshot.MountsResponse, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("prepare", prepareOpts) | ||||
| 	if err := f.getError("prepare"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	_, ok := f.MountList[prepareOpts.Key] | ||||
| 	if ok { | ||||
| 		return nil, fmt.Errorf("mounts already exist") | ||||
| 	} | ||||
| 	f.MountList[prepareOpts.Key] = []*mount.Mount{{ | ||||
| 		Type:   "bind", | ||||
| 		Source: prepareOpts.Key, | ||||
| 		// TODO(random-liu): Fake options based on Readonly option. | ||||
| 	}} | ||||
| 	return &snapshot.MountsResponse{ | ||||
| 		Mounts: f.MountList[prepareOpts.Key], | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // Mounts is a test implementation of snapshot.Mounts | ||||
| func (f *FakeSnapshotClient) Mounts(ctx context.Context, mountsOpts *snapshot.MountsRequest, opts ...grpc.CallOption) (*snapshot.MountsResponse, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("mounts", mountsOpts) | ||||
| 	if err := f.getError("mounts"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	mounts, ok := f.MountList[mountsOpts.Key] | ||||
| 	if !ok { | ||||
| 		return nil, SnapshotNotExistError | ||||
| 	} | ||||
| 	return &snapshot.MountsResponse{ | ||||
| 		Mounts: mounts, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // Commit is a test implementation of snapshot.Commit | ||||
| func (f *FakeSnapshotClient) Commit(ctx context.Context, in *snapshot.CommitRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| // View is a test implementation of snapshot.View | ||||
| func (f *FakeSnapshotClient) View(ctx context.Context, viewOpts *snapshot.PrepareRequest, opts ...grpc.CallOption) (*snapshot.MountsResponse, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("view", viewOpts) | ||||
| 	if err := f.getError("view"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	_, ok := f.MountList[viewOpts.Key] | ||||
| 	if ok { | ||||
| 		return nil, fmt.Errorf("mounts already exist") | ||||
| 	} | ||||
| 	f.MountList[viewOpts.Key] = []*mount.Mount{{ | ||||
| 		Type:   "bind", | ||||
| 		Source: viewOpts.Key, | ||||
| 		// TODO(random-liu): Fake options based on Readonly option. | ||||
| 	}} | ||||
| 	return &snapshot.MountsResponse{ | ||||
| 		Mounts: f.MountList[viewOpts.Key], | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // Remove is a test implementation of snapshot.Remove | ||||
| func (f *FakeSnapshotClient) Remove(ctx context.Context, removeOpts *snapshot.RemoveRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { | ||||
| 	f.Lock() | ||||
| 	defer f.Unlock() | ||||
| 	f.appendCalled("remove", removeOpts) | ||||
| 	if err := f.getError("remove"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if _, ok := f.MountList[removeOpts.Key]; !ok { | ||||
| 		return nil, SnapshotNotExistError | ||||
| 	} | ||||
| 	delete(f.MountList, removeOpts.Key) | ||||
| 	return &google_protobuf1.Empty{}, nil | ||||
| } | ||||
|  | ||||
| // Stat is a test implementation of snapshot.Stat | ||||
| func (f *FakeSnapshotClient) Stat(ctx context.Context, in *snapshot.StatRequest, opts ...grpc.CallOption) (*snapshot.StatResponse, error) { | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| // List is a test implementation of snapshot.List | ||||
| func (f *FakeSnapshotClient) List(ctx context.Context, in *snapshot.ListRequest, opts ...grpc.CallOption) (snapshot.Snapshot_ListClient, error) { | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| // Usage is a test implementation of snapshot.Usage | ||||
| func (f *FakeSnapshotClient) Usage(ctx context.Context, in *snapshot.UsageRequest, opts ...grpc.CallOption) (*snapshot.UsageResponse, error) { | ||||
| 	return nil, nil | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Lantao Liu
					Lantao Liu