Add unit test.

Signed-off-by: Random-Liu <lantaol@google.com>
This commit is contained in:
Random-Liu 2017-05-12 13:14:25 -07:00
parent bf28c7fc75
commit e4e9f30c5d
8 changed files with 1096 additions and 0 deletions

View File

@ -0,0 +1,54 @@
/*
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 registrar
import (
"testing"
assertlib "github.com/stretchr/testify/assert"
)
func TestRegistrar(t *testing.T) {
r := NewRegistrar()
assert := assertlib.New(t)
t.Logf("should be able to reserve a name<->key mapping")
assert.NoError(r.Reserve("test-name-1", "test-id-1"))
t.Logf("should be able to reserve a new name<->key mapping")
assert.NoError(r.Reserve("test-name-2", "test-id-2"))
t.Logf("should be able to reserve the same name<->key mapping")
assert.NoError(r.Reserve("test-name-1", "test-id-1"))
t.Logf("should not be able to reserve conflict name<->key mapping")
assert.Error(r.Reserve("test-name-1", "test-id-conflict"))
assert.Error(r.Reserve("test-name-conflict", "test-id-2"))
t.Logf("should be able to release name<->key mapping by key")
r.ReleaseByKey("test-id-1")
t.Logf("should be able to release name<->key mapping by name")
r.ReleaseByName("test-name-2")
t.Logf("should be able to reserve new name<->key mapping after release")
assert.NoError(r.Reserve("test-name-1", "test-id-new"))
assert.NoError(r.Reserve("test-name-new", "test-id-2"))
t.Logf("should be able to reserve same name/key name<->key")
assert.NoError(r.Reserve("same-name-id", "same-name-id"))
}

View File

@ -0,0 +1,59 @@
/*
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 (
"testing"
"github.com/stretchr/testify/assert"
"github.com/kubernetes-incubator/cri-containerd/pkg/metadata"
)
func TestGetSandbox(t *testing.T) {
c := newTestCRIContainerdService()
testID := "abcdefg"
testSandbox := metadata.SandboxMetadata{
ID: testID,
Name: "test-name",
}
assert.NoError(t, c.sandboxStore.Create(testSandbox))
assert.NoError(t, c.sandboxIDIndex.Add(testID))
for desc, test := range map[string]struct {
id string
expected *metadata.SandboxMetadata
}{
"full id": {
id: testID,
expected: &testSandbox,
},
"partial id": {
id: testID[:3],
expected: &testSandbox,
},
"non-exist id": {
id: "gfedcba",
expected: nil,
},
} {
t.Logf("TestCase %q", desc)
sb, err := c.getSandbox(test.id)
assert.NoError(t, err)
assert.Equal(t, test.expected, sb)
}
}

View File

@ -0,0 +1,210 @@
/*
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 (
"testing"
"time"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
"github.com/containerd/containerd/api/types/container"
"k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
"github.com/kubernetes-incubator/cri-containerd/pkg/metadata"
servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing"
)
func TestToCRISandbox(t *testing.T) {
config := &runtime.PodSandboxConfig{
Metadata: &runtime.PodSandboxMetadata{
Name: "test-name",
Uid: "test-uid",
Namespace: "test-ns",
Attempt: 1,
},
Labels: map[string]string{"a": "b"},
Annotations: map[string]string{"c": "d"},
}
createdAt := time.Now().UnixNano()
meta := &metadata.SandboxMetadata{
ID: "test-id",
Name: "test-name",
Config: config,
CreatedAt: createdAt,
NetNS: "test-netns",
}
state := runtime.PodSandboxState_SANDBOX_READY
expect := &runtime.PodSandbox{
Id: "test-id",
Metadata: config.GetMetadata(),
State: state,
CreatedAt: createdAt,
Labels: config.GetLabels(),
Annotations: config.GetAnnotations(),
}
s := toCRISandbox(meta, state)
assert.Equal(t, expect, s)
}
func TestFilterSandboxes(t *testing.T) {
c := newTestCRIContainerdService()
testSandboxes := []*runtime.PodSandbox{
{
Id: "1",
Metadata: &runtime.PodSandboxMetadata{Name: "name-1", Uid: "uid-1", Namespace: "ns-1", Attempt: 1},
State: runtime.PodSandboxState_SANDBOX_READY,
},
{
Id: "2",
Metadata: &runtime.PodSandboxMetadata{Name: "name-2", Uid: "uid-2", Namespace: "ns-2", Attempt: 2},
State: runtime.PodSandboxState_SANDBOX_NOTREADY,
Labels: map[string]string{"a": "b"},
},
{
Id: "3",
Metadata: &runtime.PodSandboxMetadata{Name: "name-2", Uid: "uid-2", Namespace: "ns-2", Attempt: 2},
State: runtime.PodSandboxState_SANDBOX_READY,
Labels: map[string]string{"c": "d"},
},
}
for desc, test := range map[string]struct {
filter *runtime.PodSandboxFilter
expect []*runtime.PodSandbox
}{
"no filter": {
expect: testSandboxes,
},
"id filter": {
filter: &runtime.PodSandboxFilter{Id: "2"},
expect: []*runtime.PodSandbox{testSandboxes[1]},
},
"state filter": {
filter: &runtime.PodSandboxFilter{
State: &runtime.PodSandboxStateValue{
State: runtime.PodSandboxState_SANDBOX_READY,
},
},
expect: []*runtime.PodSandbox{testSandboxes[0], testSandboxes[2]},
},
"label filter": {
filter: &runtime.PodSandboxFilter{
LabelSelector: map[string]string{"a": "b"},
},
expect: []*runtime.PodSandbox{testSandboxes[1]},
},
"mixed filter not matched": {
filter: &runtime.PodSandboxFilter{
Id: "1",
LabelSelector: map[string]string{"a": "b"},
},
expect: []*runtime.PodSandbox{},
},
"mixed filter matched": {
filter: &runtime.PodSandboxFilter{
State: &runtime.PodSandboxStateValue{
State: runtime.PodSandboxState_SANDBOX_READY,
},
LabelSelector: map[string]string{"c": "d"},
},
expect: []*runtime.PodSandbox{testSandboxes[2]},
},
} {
filtered := c.filterCRISandboxes(testSandboxes, test.filter)
assert.Equal(t, test.expect, filtered, desc)
}
}
func TestListPodSandbox(t *testing.T) {
c := newTestCRIContainerdService()
fake := c.containerService.(*servertesting.FakeExecutionClient)
sandboxesInStore := []metadata.SandboxMetadata{
{
ID: "1",
Name: "name-1",
Config: &runtime.PodSandboxConfig{Metadata: &runtime.PodSandboxMetadata{Name: "name-1"}},
},
{
ID: "2",
Name: "name-2",
Config: &runtime.PodSandboxConfig{Metadata: &runtime.PodSandboxMetadata{Name: "name-2"}},
},
{
ID: "3",
Name: "name-3",
Config: &runtime.PodSandboxConfig{Metadata: &runtime.PodSandboxMetadata{Name: "name-3"}},
},
}
sandboxesInContainerd := []container.Container{
// Running container with corresponding metadata
{
ID: "1",
Pid: 1,
Status: container.Status_RUNNING,
},
// Stopped container with corresponding metadata
{
ID: "2",
Pid: 2,
Status: container.Status_STOPPED,
},
// Container without corresponding metadata
{
ID: "4",
Pid: 4,
Status: container.Status_STOPPED,
},
}
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 {
c.sandboxStore.Create(s)
}
// Inject fake containerd containers
fake.SetFakeContainers(sandboxesInContainerd)
resp, err := c.ListPodSandbox(context.Background(), &runtime.ListPodSandboxRequest{})
assert.NoError(t, err)
sandboxes := resp.GetItems()
assert.Len(t, sandboxes, len(expect))
for _, s := range expect {
assert.Contains(t, sandboxes, s)
}
}

View File

@ -0,0 +1,122 @@
/*
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"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
"github.com/containerd/containerd/api/types/container"
"github.com/kubernetes-incubator/cri-containerd/pkg/metadata"
ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing"
servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing"
"k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
)
func TestRemovePodSandbox(t *testing.T) {
testID := "test-id"
testName := "test-name"
testMetadata := metadata.SandboxMetadata{
ID: testID,
Name: testName,
}
for desc, test := range map[string]struct {
sandboxContainers []container.Container
injectMetadata bool
injectContainerdErr error
injectFSErr error
expectErr bool
expectRemoved string
expectCalls []string
}{
"should not return error if sandbox does not exist": {
injectMetadata: false,
expectErr: false,
expectCalls: []string{},
},
"should return error when sandbox container is not deleted": {
injectMetadata: true,
sandboxContainers: []container.Container{{ID: testID}},
expectErr: true,
expectCalls: []string{"info"},
},
"should return error when arbitrary containerd error is injected": {
injectMetadata: true,
injectContainerdErr: fmt.Errorf("arbitrary error"),
expectErr: true,
expectCalls: []string{"info"},
},
"should return error when error fs error is injected": {
injectMetadata: true,
injectFSErr: fmt.Errorf("fs error"),
expectRemoved: getSandboxRootDir(testRootDir, testID),
expectErr: true,
expectCalls: []string{"info"},
},
"should be able to successfully delete": {
injectMetadata: true,
expectRemoved: getSandboxRootDir(testRootDir, testID),
expectCalls: []string{"info"},
},
} {
t.Logf("TestCase %q", desc)
c := newTestCRIContainerdService()
fake := c.containerService.(*servertesting.FakeExecutionClient)
fakeOS := c.os.(*ostesting.FakeOS)
fake.SetFakeContainers(test.sandboxContainers)
if test.injectMetadata {
c.sandboxNameIndex.Reserve(testName, testID)
c.sandboxIDIndex.Add(testID)
c.sandboxStore.Create(testMetadata)
}
if test.injectContainerdErr != nil {
fake.InjectError("info", test.injectContainerdErr)
}
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, fake.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.sandboxIDIndex.Get(testID)
assert.Error(t, err, "sandbox id should be removed")
meta, err := c.sandboxStore.Get(testID)
assert.NoError(t, err)
assert.Nil(t, meta, "sandbox metadata should be removed")
res, err = c.RemovePodSandbox(context.Background(), &runtime.RemovePodSandboxRequest{
PodSandboxId: testID,
})
assert.NoError(t, err)
assert.NotNil(t, res, "remove should be idempotent")
}
}

View File

@ -0,0 +1,191 @@
/*
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 (
"encoding/json"
"io"
"os"
"syscall"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/net/context"
"github.com/containerd/containerd/api/services/execution"
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing"
servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing"
"k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
)
func getRunPodSandboxTestData() (*runtime.PodSandboxConfig, func(*testing.T, string, *runtimespec.Spec)) {
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"},
Linux: &runtime.LinuxPodSandboxConfig{
CgroupParent: "/test/cgroup/parent",
},
}
specCheck := func(t *testing.T, id string, spec *runtimespec.Spec) {
assert.Equal(t, "test-hostname", spec.Hostname)
assert.Equal(t, getCgroupsPath("/test/cgroup/parent", id), spec.Linux.CgroupsPath)
assert.Equal(t, relativeRootfsPath, spec.Root.Path)
assert.Equal(t, true, spec.Root.Readonly)
}
return config, specCheck
}
func TestGenerateSandboxContainerSpec(t *testing.T) {
testID := "test-id"
for desc, test := range map[string]struct {
configChange func(*runtime.PodSandboxConfig)
specCheck func(*testing.T, *runtimespec.Spec)
}{
"spec should reflect original config": {
specCheck: func(t *testing.T, spec *runtimespec.Spec) {
// runtime spec should have expected namespaces enabled by default.
require.NotNil(t, spec.Linux)
assert.Contains(t, spec.Linux.Namespaces, runtimespec.LinuxNamespace{
Type: runtimespec.NetworkNamespace,
})
assert.Contains(t, spec.Linux.Namespaces, runtimespec.LinuxNamespace{
Type: runtimespec.PIDNamespace,
})
assert.Contains(t, spec.Linux.Namespaces, runtimespec.LinuxNamespace{
Type: runtimespec.IPCNamespace,
})
},
},
"host namespace": {
configChange: func(c *runtime.PodSandboxConfig) {
c.Linux.SecurityContext = &runtime.LinuxSandboxSecurityContext{
NamespaceOptions: &runtime.NamespaceOption{
HostNetwork: true,
HostPid: true,
HostIpc: true,
},
}
},
specCheck: func(t *testing.T, spec *runtimespec.Spec) {
// runtime spec should disable expected namespaces in host mode.
require.NotNil(t, spec.Linux)
assert.NotContains(t, spec.Linux.Namespaces, runtimespec.LinuxNamespace{
Type: runtimespec.NetworkNamespace,
})
assert.NotContains(t, spec.Linux.Namespaces, runtimespec.LinuxNamespace{
Type: runtimespec.PIDNamespace,
})
assert.NotContains(t, spec.Linux.Namespaces, runtimespec.LinuxNamespace{
Type: runtimespec.IPCNamespace,
})
},
},
} {
t.Logf("TestCase %q", desc)
c := newTestCRIContainerdService()
config, specCheck := getRunPodSandboxTestData()
if test.configChange != nil {
test.configChange(config)
}
spec := c.generateSandboxContainerSpec(testID, config)
specCheck(t, testID, spec)
if test.specCheck != nil {
test.specCheck(t, spec)
}
}
}
func TestRunPodSandbox(t *testing.T) {
config, specCheck := getRunPodSandboxTestData()
c := newTestCRIContainerdService()
fake := c.containerService.(*servertesting.FakeExecutionClient)
fakeOS := c.os.(*ostesting.FakeOS)
var dirs []string
var pipes []string
fakeOS.MkdirAllFn = func(path string, perm os.FileMode) error {
dirs = append(dirs, path)
assert.Equal(t, os.FileMode(0755), perm)
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
}
expectCalls := []string{"create", "start"}
res, err := c.RunPodSandbox(context.Background(), &runtime.RunPodSandboxRequest{Config: config})
assert.NoError(t, err)
require.NotNil(t, res)
id := res.GetPodSandboxId()
assert.Len(t, dirs, 1)
assert.Equal(t, getSandboxRootDir(c.rootDir, id), dirs[0], "sandbox root directory should be created")
assert.Len(t, pipes, 2)
_, stdout, stderr := getStreamingPipes(getSandboxRootDir(c.rootDir, id))
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, expectCalls, fake.GetCalledNames(), "expect containerd functions should be called")
calls := fake.GetCalledDetails()
createOpts := calls[0].Argument.(*execution.CreateRequest)
assert.Equal(t, id, createOpts.ID, "create id should be correct")
// TODO(random-liu): Test rootfs mount when image management part is integrated.
assert.Equal(t, stdout, createOpts.Stdout, "stdout pipe should be passed to containerd")
assert.Equal(t, stderr, createOpts.Stderr, "stderr pipe should be passed to containerd")
spec := &runtimespec.Spec{}
assert.NoError(t, json.Unmarshal(createOpts.Spec.Value, spec))
t.Logf("oci spec check")
specCheck(t, id, spec)
startID := calls[1].Argument.(*execution.StartRequest).ID
assert.Equal(t, id, startID, "start id should be correct")
meta, err := c.sandboxStore.Get(id)
assert.NoError(t, err)
assert.Equal(t, id, meta.ID, "metadata id should be correct")
err = c.sandboxNameIndex.Reserve(meta.Name, "random-id")
assert.Error(t, err, "metadata name should be reserved")
assert.Equal(t, config, meta.Config, "metadata config should be correct")
// TODO(random-liu): [P2] Add clock interface and use fake clock.
assert.NotZero(t, meta.CreatedAt, "metadata CreatedAt should be set")
info, err := fake.Info(context.Background(), &execution.InfoRequest{ID: id})
assert.NoError(t, err)
pid := info.Pid
assert.Equal(t, meta.NetNS, getNetworkNamespace(pid), "metadata network namespace should be correct")
gotID, err := c.sandboxIDIndex.Get(id)
assert.NoError(t, err)
assert.Equal(t, id, gotID, "sandbox id should be indexed")
}
// TODO(random-liu): [P1] Add unit test for different error cases to make sure
// the function cleans up on error properly.

View File

@ -0,0 +1,192 @@
/*
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/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/net/context"
"github.com/containerd/containerd/api/types/container"
"k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
"github.com/kubernetes-incubator/cri-containerd/pkg/metadata"
servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing"
)
// Variables used in the following test.
const sandboxStatusTestID = "test-id"
func getSandboxStatusTestData() (*metadata.SandboxMetadata, *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()
metadata := &metadata.SandboxMetadata{
ID: sandboxStatusTestID,
Name: "test-name",
Config: config,
CreatedAt: createdAt,
}
expectedStatus := &runtime.PodSandboxStatus{
Id: sandboxStatusTestID,
Metadata: config.GetMetadata(),
CreatedAt: createdAt,
Network: &runtime.PodSandboxNetworkStatus{},
Linux: &runtime.LinuxPodSandboxStatus{
Namespaces: &runtime.Namespace{
Options: &runtime.NamespaceOption{
HostNetwork: true,
HostPid: false,
HostIpc: true,
},
},
},
Labels: config.GetLabels(),
Annotations: config.GetAnnotations(),
}
return metadata, expectedStatus
}
func TestToCRISandboxStatus(t *testing.T) {
for desc, test := range map[string]struct {
state runtime.PodSandboxState
expectNetNS string
}{
"ready sandbox should have network namespace": {
state: runtime.PodSandboxState_SANDBOX_READY,
expectNetNS: "test-netns",
},
"not ready sandbox should not have network namespace": {
state: runtime.PodSandboxState_SANDBOX_NOTREADY,
expectNetNS: "",
},
} {
metadata, expect := getSandboxStatusTestData()
metadata.NetNS = "test-netns"
status := toCRISandboxStatus(metadata, test.state)
expect.Linux.Namespaces.Network = test.expectNetNS
expect.State = test.state
assert.Equal(t, expect, status, desc)
}
}
func TestPodSandboxStatus(t *testing.T) {
for desc, test := range map[string]struct {
sandboxContainers []container.Container
injectMetadata bool
injectErr error
expectState runtime.PodSandboxState
expectErr bool
expectCalls []string
}{
"sandbox status without metadata": {
injectMetadata: false,
expectErr: true,
expectCalls: []string{},
},
"sandbox status with running sandbox container": {
sandboxContainers: []container.Container{{
ID: sandboxStatusTestID,
Pid: 1,
Status: container.Status_RUNNING,
}},
injectMetadata: true,
expectState: runtime.PodSandboxState_SANDBOX_READY,
expectCalls: []string{"info"},
},
"sandbox status with stopped sandbox container": {
sandboxContainers: []container.Container{{
ID: sandboxStatusTestID,
Pid: 1,
Status: container.Status_STOPPED,
}},
injectMetadata: true,
expectState: runtime.PodSandboxState_SANDBOX_NOTREADY,
expectCalls: []string{"info"},
},
"sandbox status with non-existing sandbox container": {
sandboxContainers: []container.Container{},
injectMetadata: true,
expectState: runtime.PodSandboxState_SANDBOX_NOTREADY,
expectCalls: []string{"info"},
},
"sandbox status with arbitrary error": {
sandboxContainers: []container.Container{{
ID: sandboxStatusTestID,
Pid: 1,
Status: container.Status_RUNNING,
}},
injectMetadata: true,
injectErr: errors.New("arbitrary error"),
expectErr: true,
expectCalls: []string{"info"},
},
} {
t.Logf("TestCase %q", desc)
metadata, expect := getSandboxStatusTestData()
c := newTestCRIContainerdService()
fake := c.containerService.(*servertesting.FakeExecutionClient)
fake.SetFakeContainers(test.sandboxContainers)
if test.injectMetadata {
assert.NoError(t, c.sandboxIDIndex.Add(metadata.ID))
assert.NoError(t, c.sandboxStore.Create(*metadata))
}
if test.injectErr != nil {
fake.InjectError("info", test.injectErr)
}
res, err := c.PodSandboxStatus(context.Background(), &runtime.PodSandboxStatusRequest{
PodSandboxId: sandboxStatusTestID,
})
assert.Equal(t, test.expectCalls, fake.GetCalledNames())
if test.expectErr {
assert.Error(t, err)
assert.Nil(t, res)
continue
}
assert.NoError(t, err)
require.NotNil(t, res)
expect.State = test.expectState
assert.Equal(t, expect, res.GetStatus())
}
}

View File

@ -0,0 +1,106 @@
/*
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 (
"testing"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"github.com/containerd/containerd"
"github.com/containerd/containerd/api/types/container"
"k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
"github.com/kubernetes-incubator/cri-containerd/pkg/metadata"
servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing"
)
func TestStopPodSandbox(t *testing.T) {
testID := "test-id"
testSandbox := metadata.SandboxMetadata{
ID: testID,
Name: "test-name",
}
testContainer := container.Container{
ID: testID,
Pid: 1,
Status: container.Status_RUNNING,
}
for desc, test := range map[string]struct {
sandboxContainers []container.Container
injectSandbox bool
injectErr error
expectErr bool
expectCalls []string
}{
"stop non-existing sandbox": {
injectSandbox: false,
expectErr: true,
expectCalls: []string{},
},
"stop sandbox with sandbox container": {
sandboxContainers: []container.Container{testContainer},
injectSandbox: true,
expectErr: false,
expectCalls: []string{"delete"},
},
"stop sandbox with sandbox container not exist error": {
sandboxContainers: []container.Container{},
injectSandbox: true,
// Inject error to make sure fake execution client returns error.
injectErr: grpc.Errorf(codes.Unknown, containerd.ErrContainerNotExist.Error()),
expectErr: false,
expectCalls: []string{"delete"},
},
"stop sandbox with with arbitrary error": {
injectSandbox: true,
injectErr: grpc.Errorf(codes.Unknown, "arbitrary error"),
expectErr: true,
expectCalls: []string{"delete"},
},
} {
t.Logf("TestCase %q", desc)
c := newTestCRIContainerdService()
fake := c.containerService.(*servertesting.FakeExecutionClient)
fake.SetFakeContainers(test.sandboxContainers)
if test.injectSandbox {
assert.NoError(t, c.sandboxStore.Create(testSandbox))
c.sandboxIDIndex.Add(testID)
}
if test.injectErr != nil {
fake.InjectError("delete", test.injectErr)
}
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())
}
}

162
pkg/server/service_test.go Normal file
View File

@ -0,0 +1,162 @@
/*
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 (
"io"
"os"
"testing"
"github.com/docker/docker/pkg/truncindex"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/net/context"
"github.com/kubernetes-incubator/cri-containerd/pkg/metadata"
"github.com/kubernetes-incubator/cri-containerd/pkg/metadata/store"
ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing"
"github.com/kubernetes-incubator/cri-containerd/pkg/registrar"
servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing"
"github.com/containerd/containerd/api/services/execution"
"k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
)
type nopReadWriteCloser struct{}
func (nopReadWriteCloser) Read(p []byte) (n int, err error) { return len(p), nil }
func (nopReadWriteCloser) Write(p []byte) (n int, err error) { return len(p), nil }
func (nopReadWriteCloser) Close() error { return nil }
const testRootDir = "/test/rootfs"
// newTestCRIContainerdService creates a fake criContainerdService for test.
func newTestCRIContainerdService() *criContainerdService {
return &criContainerdService{
os: ostesting.NewFakeOS(),
rootDir: testRootDir,
containerService: servertesting.NewFakeExecutionClient(),
sandboxStore: metadata.NewSandboxStore(store.NewMetadataStore()),
sandboxNameIndex: registrar.NewRegistrar(),
sandboxIDIndex: truncindex.NewTruncIndex(nil),
}
}
// Test all sandbox operations.
func TestSandboxOperations(t *testing.T) {
c := newTestCRIContainerdService()
fake := c.containerService.(*servertesting.FakeExecutionClient)
fakeOS := c.os.(*ostesting.FakeOS)
fakeOS.OpenFifoFn = func(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
return nopReadWriteCloser{}, nil
}
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{ID: id})
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{
Network: getNetworkNamespace(info.Pid),
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()
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_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)
}