Add unit test
Signed-off-by: Xianglin Gao <xlgao@zju.edu.cn>
This commit is contained in:
parent
6d2b9fabca
commit
4a4414987f
@ -42,8 +42,8 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Exitf("Failed to connect containerd endpoint %q: %v", o.ContainerdEndpoint, err)
|
glog.Exitf("Failed to connect containerd endpoint %q: %v", o.ContainerdEndpoint, err)
|
||||||
}
|
}
|
||||||
glog.V(2).Infof("Run cri-containerd grpc server on socket %q", o.SocketPath)
|
|
||||||
|
|
||||||
|
glog.V(2).Infof("Run cri-containerd grpc server on socket %q", o.SocketPath)
|
||||||
service, err := server.NewCRIContainerdService(conn, o.RootDir, o.NetworkPluginBinDir, o.NetworkPluginConfDir)
|
service, err := server.NewCRIContainerdService(conn, o.RootDir, o.NetworkPluginBinDir, o.NetworkPluginConfDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Exitf("Failed to create CRI containerd service %+v: %v", o, err)
|
glog.Exitf("Failed to create CRI containerd service %+v: %v", o, err)
|
||||||
|
@ -19,6 +19,7 @@ package testing
|
|||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
@ -29,20 +30,64 @@ import (
|
|||||||
// If a member of the form `*Fn` is set, that function will be called in place
|
// If a member of the form `*Fn` is set, that function will be called in place
|
||||||
// of the real call.
|
// of the real call.
|
||||||
type FakeOS struct {
|
type FakeOS struct {
|
||||||
|
sync.Mutex
|
||||||
MkdirAllFn func(string, os.FileMode) error
|
MkdirAllFn func(string, os.FileMode) error
|
||||||
RemoveAllFn func(string) error
|
RemoveAllFn func(string) error
|
||||||
OpenFifoFn func(context.Context, string, int, os.FileMode) (io.ReadWriteCloser, error)
|
OpenFifoFn func(context.Context, string, int, os.FileMode) (io.ReadWriteCloser, error)
|
||||||
|
StatFn func(name string) (os.FileInfo, error)
|
||||||
|
errors map[string]error
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ osInterface.OS = &FakeOS{}
|
var _ osInterface.OS = &FakeOS{}
|
||||||
|
|
||||||
|
// getError get error for call
|
||||||
|
func (f *FakeOS) getError(op string) error {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
err, ok := f.errors[op]
|
||||||
|
if ok {
|
||||||
|
delete(f.errors, op)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InjectError inject error for call
|
||||||
|
func (f *FakeOS) InjectError(fn string, err error) {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
f.errors[fn] = err
|
||||||
|
}
|
||||||
|
|
||||||
|
// InjectErrors inject errors for calls
|
||||||
|
func (f *FakeOS) 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 *FakeOS) ClearErrors() {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
f.errors = make(map[string]error)
|
||||||
|
}
|
||||||
|
|
||||||
// NewFakeOS creates a FakeOS.
|
// NewFakeOS creates a FakeOS.
|
||||||
func NewFakeOS() *FakeOS {
|
func NewFakeOS() *FakeOS {
|
||||||
return &FakeOS{}
|
return &FakeOS{
|
||||||
|
errors: make(map[string]error),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MkdirAll is a fake call that invokes MkdirAllFn or just returns nil.
|
// MkdirAll is a fake call that invokes MkdirAllFn or just returns nil.
|
||||||
func (f *FakeOS) MkdirAll(path string, perm os.FileMode) error {
|
func (f *FakeOS) MkdirAll(path string, perm os.FileMode) error {
|
||||||
|
if err := f.getError("MkdirAll"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if f.MkdirAllFn != nil {
|
if f.MkdirAllFn != nil {
|
||||||
return f.MkdirAllFn(path, perm)
|
return f.MkdirAllFn(path, perm)
|
||||||
}
|
}
|
||||||
@ -51,6 +96,10 @@ func (f *FakeOS) MkdirAll(path string, perm os.FileMode) error {
|
|||||||
|
|
||||||
// RemoveAll is a fake call that invokes RemoveAllFn or just returns nil.
|
// RemoveAll is a fake call that invokes RemoveAllFn or just returns nil.
|
||||||
func (f *FakeOS) RemoveAll(path string) error {
|
func (f *FakeOS) RemoveAll(path string) error {
|
||||||
|
if err := f.getError("RemoveAll"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if f.RemoveAllFn != nil {
|
if f.RemoveAllFn != nil {
|
||||||
return f.RemoveAllFn(path)
|
return f.RemoveAllFn(path)
|
||||||
}
|
}
|
||||||
@ -59,8 +108,24 @@ func (f *FakeOS) RemoveAll(path string) error {
|
|||||||
|
|
||||||
// OpenFifo is a fake call that invokes OpenFifoFn or just returns nil.
|
// OpenFifo is a fake call that invokes OpenFifoFn or just returns nil.
|
||||||
func (f *FakeOS) OpenFifo(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
|
func (f *FakeOS) OpenFifo(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
|
||||||
|
if err := f.getError("OpenFifo"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if f.OpenFifoFn != nil {
|
if f.OpenFifoFn != nil {
|
||||||
return f.OpenFifoFn(ctx, fn, flag, perm)
|
return f.OpenFifoFn(ctx, fn, flag, perm)
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stat is a fake call that invokes Stat or just return nil.
|
||||||
|
func (f *FakeOS) Stat(name string) (os.FileInfo, error) {
|
||||||
|
if err := f.getError("Stat"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.StatFn != nil {
|
||||||
|
return f.StatFn(name)
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
@ -164,7 +164,7 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run
|
|||||||
defer func() {
|
defer func() {
|
||||||
if retErr != nil {
|
if retErr != nil {
|
||||||
// Cleanup the sandbox container if an error is returned.
|
// Cleanup the sandbox container if an error is returned.
|
||||||
if _, err := c.containerService.Delete(ctx, &execution.DeleteRequest{ID: id}); err != nil {
|
if _, err = c.containerService.Delete(ctx, &execution.DeleteRequest{ID: id}); err != nil {
|
||||||
glog.Errorf("Failed to delete sandbox container %q: %v",
|
glog.Errorf("Failed to delete sandbox container %q: %v",
|
||||||
id, err)
|
id, err)
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,8 @@ func TestGenerateSandboxContainerSpec(t *testing.T) {
|
|||||||
func TestRunPodSandbox(t *testing.T) {
|
func TestRunPodSandbox(t *testing.T) {
|
||||||
config, specCheck := getRunPodSandboxTestData()
|
config, specCheck := getRunPodSandboxTestData()
|
||||||
c := newTestCRIContainerdService()
|
c := newTestCRIContainerdService()
|
||||||
fake := c.containerService.(*servertesting.FakeExecutionClient)
|
fakeExecutionClient := c.containerService.(*servertesting.FakeExecutionClient)
|
||||||
|
fakeCNIPlugin := c.netPlugin.(*servertesting.FakeCNIPlugin)
|
||||||
fakeOS := c.os.(*ostesting.FakeOS)
|
fakeOS := c.os.(*ostesting.FakeOS)
|
||||||
var dirs []string
|
var dirs []string
|
||||||
var pipes []string
|
var pipes []string
|
||||||
@ -139,7 +140,7 @@ func TestRunPodSandbox(t *testing.T) {
|
|||||||
assert.Equal(t, os.FileMode(0700), perm)
|
assert.Equal(t, os.FileMode(0700), perm)
|
||||||
return nopReadWriteCloser{}, nil
|
return nopReadWriteCloser{}, nil
|
||||||
}
|
}
|
||||||
expectCalls := []string{"create", "start"}
|
expectExecutionClientCalls := []string{"create", "start"}
|
||||||
|
|
||||||
res, err := c.RunPodSandbox(context.Background(), &runtime.RunPodSandboxRequest{Config: config})
|
res, err := c.RunPodSandbox(context.Background(), &runtime.RunPodSandboxRequest{Config: config})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -154,8 +155,8 @@ func TestRunPodSandbox(t *testing.T) {
|
|||||||
assert.Contains(t, pipes, stdout, "sandbox stdout pipe should be created")
|
assert.Contains(t, pipes, stdout, "sandbox stdout pipe should be created")
|
||||||
assert.Contains(t, pipes, stderr, "sandbox stderr 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")
|
assert.Equal(t, expectExecutionClientCalls, fakeExecutionClient.GetCalledNames(), "expect containerd functions should be called")
|
||||||
calls := fake.GetCalledDetails()
|
calls := fakeExecutionClient.GetCalledDetails()
|
||||||
createOpts := calls[0].Argument.(*execution.CreateRequest)
|
createOpts := calls[0].Argument.(*execution.CreateRequest)
|
||||||
assert.Equal(t, id, createOpts.ID, "create id should be correct")
|
assert.Equal(t, id, createOpts.ID, "create id should be correct")
|
||||||
// TODO(random-liu): Test rootfs mount when image management part is integrated.
|
// TODO(random-liu): Test rootfs mount when image management part is integrated.
|
||||||
@ -177,7 +178,7 @@ func TestRunPodSandbox(t *testing.T) {
|
|||||||
assert.Equal(t, config, meta.Config, "metadata config should be correct")
|
assert.Equal(t, config, meta.Config, "metadata config should be correct")
|
||||||
// TODO(random-liu): [P2] Add clock interface and use fake clock.
|
// TODO(random-liu): [P2] Add clock interface and use fake clock.
|
||||||
assert.NotZero(t, meta.CreatedAt, "metadata CreatedAt should be set")
|
assert.NotZero(t, meta.CreatedAt, "metadata CreatedAt should be set")
|
||||||
info, err := fake.Info(context.Background(), &execution.InfoRequest{ID: id})
|
info, err := fakeExecutionClient.Info(context.Background(), &execution.InfoRequest{ID: id})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
pid := info.Pid
|
pid := info.Pid
|
||||||
assert.Equal(t, meta.NetNS, getNetworkNamespace(pid), "metadata network namespace should be correct")
|
assert.Equal(t, meta.NetNS, getNetworkNamespace(pid), "metadata network namespace should be correct")
|
||||||
@ -185,6 +186,13 @@ func TestRunPodSandbox(t *testing.T) {
|
|||||||
gotID, err := c.sandboxIDIndex.Get(id)
|
gotID, err := c.sandboxIDIndex.Get(id)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, id, gotID, "sandbox id should be indexed")
|
assert.Equal(t, id, gotID, "sandbox id should be indexed")
|
||||||
|
|
||||||
|
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{meta.NetNS, config.GetMetadata().GetNamespace(), config.GetMetadata().GetName(), id}
|
||||||
|
assert.Equal(t, expectedPluginArgument, pluginArgument, "SetUpPod should be called with correct arguments")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(random-liu): [P1] Add unit test for different error cases to make sure
|
// TODO(random-liu): [P1] Add unit test for different error cases to make sure
|
||||||
|
@ -35,7 +35,11 @@ import (
|
|||||||
|
|
||||||
// Variables used in the following test.
|
// Variables used in the following test.
|
||||||
|
|
||||||
const sandboxStatusTestID = "test-id"
|
const (
|
||||||
|
sandboxStatusTestID = "test-id"
|
||||||
|
sandboxStatusTestIP = "10.10.10.10"
|
||||||
|
sandboxStatusTestNetNS = "test-netns"
|
||||||
|
)
|
||||||
|
|
||||||
func getSandboxStatusTestData() (*metadata.SandboxMetadata, *runtime.PodSandboxStatus) {
|
func getSandboxStatusTestData() (*metadata.SandboxMetadata, *runtime.PodSandboxStatus) {
|
||||||
config := &runtime.PodSandboxConfig{
|
config := &runtime.PodSandboxConfig{
|
||||||
@ -65,15 +69,17 @@ func getSandboxStatusTestData() (*metadata.SandboxMetadata, *runtime.PodSandboxS
|
|||||||
Name: "test-name",
|
Name: "test-name",
|
||||||
Config: config,
|
Config: config,
|
||||||
CreatedAt: createdAt,
|
CreatedAt: createdAt,
|
||||||
|
NetNS: sandboxStatusTestNetNS,
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedStatus := &runtime.PodSandboxStatus{
|
expectedStatus := &runtime.PodSandboxStatus{
|
||||||
Id: sandboxStatusTestID,
|
Id: sandboxStatusTestID,
|
||||||
Metadata: config.GetMetadata(),
|
Metadata: config.GetMetadata(),
|
||||||
CreatedAt: createdAt,
|
CreatedAt: createdAt,
|
||||||
Network: &runtime.PodSandboxNetworkStatus{},
|
Network: &runtime.PodSandboxNetworkStatus{Ip: ""},
|
||||||
Linux: &runtime.LinuxPodSandboxStatus{
|
Linux: &runtime.LinuxPodSandboxStatus{
|
||||||
Namespaces: &runtime.Namespace{
|
Namespaces: &runtime.Namespace{
|
||||||
|
Network: sandboxStatusTestNetNS,
|
||||||
Options: &runtime.NamespaceOption{
|
Options: &runtime.NamespaceOption{
|
||||||
HostNetwork: true,
|
HostNetwork: true,
|
||||||
HostPid: false,
|
HostPid: false,
|
||||||
@ -95,7 +101,7 @@ func TestToCRISandboxStatus(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
"ready sandbox should have network namespace": {
|
"ready sandbox should have network namespace": {
|
||||||
state: runtime.PodSandboxState_SANDBOX_READY,
|
state: runtime.PodSandboxState_SANDBOX_READY,
|
||||||
expectNetNS: "test-netns",
|
expectNetNS: sandboxStatusTestNetNS,
|
||||||
},
|
},
|
||||||
"not ready sandbox should not have network namespace": {
|
"not ready sandbox should not have network namespace": {
|
||||||
state: runtime.PodSandboxState_SANDBOX_NOTREADY,
|
state: runtime.PodSandboxState_SANDBOX_NOTREADY,
|
||||||
@ -103,10 +109,10 @@ func TestToCRISandboxStatus(t *testing.T) {
|
|||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
metadata, expect := getSandboxStatusTestData()
|
metadata, expect := getSandboxStatusTestData()
|
||||||
metadata.NetNS = "test-netns"
|
status := toCRISandboxStatus(metadata, test.state, sandboxStatusTestIP)
|
||||||
status := toCRISandboxStatus(metadata, test.state)
|
|
||||||
expect.Linux.Namespaces.Network = test.expectNetNS
|
expect.Linux.Namespaces.Network = test.expectNetNS
|
||||||
expect.State = test.state
|
expect.State = test.state
|
||||||
|
expect.Network.Ip = sandboxStatusTestIP
|
||||||
assert.Equal(t, expect, status, desc)
|
assert.Equal(t, expect, status, desc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,14 +122,18 @@ func TestPodSandboxStatus(t *testing.T) {
|
|||||||
sandboxContainers []container.Container
|
sandboxContainers []container.Container
|
||||||
injectMetadata bool
|
injectMetadata bool
|
||||||
injectErr error
|
injectErr error
|
||||||
|
injectIP bool
|
||||||
|
injectCNIErr error
|
||||||
expectState runtime.PodSandboxState
|
expectState runtime.PodSandboxState
|
||||||
expectErr bool
|
expectErr bool
|
||||||
expectCalls []string
|
expectCalls []string
|
||||||
|
expectedCNICalls []string
|
||||||
}{
|
}{
|
||||||
"sandbox status without metadata": {
|
"sandbox status without metadata": {
|
||||||
injectMetadata: false,
|
injectMetadata: false,
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
expectCalls: []string{},
|
expectCalls: []string{},
|
||||||
|
expectedCNICalls: []string{},
|
||||||
},
|
},
|
||||||
"sandbox status with running sandbox container": {
|
"sandbox status with running sandbox container": {
|
||||||
sandboxContainers: []container.Container{{
|
sandboxContainers: []container.Container{{
|
||||||
@ -131,9 +141,10 @@ func TestPodSandboxStatus(t *testing.T) {
|
|||||||
Pid: 1,
|
Pid: 1,
|
||||||
Status: container.Status_RUNNING,
|
Status: container.Status_RUNNING,
|
||||||
}},
|
}},
|
||||||
injectMetadata: true,
|
injectMetadata: true,
|
||||||
expectState: runtime.PodSandboxState_SANDBOX_READY,
|
expectState: runtime.PodSandboxState_SANDBOX_READY,
|
||||||
expectCalls: []string{"info"},
|
expectCalls: []string{"info"},
|
||||||
|
expectedCNICalls: []string{"GetContainerNetworkStatus"},
|
||||||
},
|
},
|
||||||
"sandbox status with stopped sandbox container": {
|
"sandbox status with stopped sandbox container": {
|
||||||
sandboxContainers: []container.Container{{
|
sandboxContainers: []container.Container{{
|
||||||
@ -141,15 +152,17 @@ func TestPodSandboxStatus(t *testing.T) {
|
|||||||
Pid: 1,
|
Pid: 1,
|
||||||
Status: container.Status_STOPPED,
|
Status: container.Status_STOPPED,
|
||||||
}},
|
}},
|
||||||
injectMetadata: true,
|
injectMetadata: true,
|
||||||
expectState: runtime.PodSandboxState_SANDBOX_NOTREADY,
|
expectState: runtime.PodSandboxState_SANDBOX_NOTREADY,
|
||||||
expectCalls: []string{"info"},
|
expectCalls: []string{"info"},
|
||||||
|
expectedCNICalls: []string{"GetContainerNetworkStatus"},
|
||||||
},
|
},
|
||||||
"sandbox status with non-existing sandbox container": {
|
"sandbox status with non-existing sandbox container": {
|
||||||
sandboxContainers: []container.Container{},
|
sandboxContainers: []container.Container{},
|
||||||
injectMetadata: true,
|
injectMetadata: true,
|
||||||
expectState: runtime.PodSandboxState_SANDBOX_NOTREADY,
|
expectState: runtime.PodSandboxState_SANDBOX_NOTREADY,
|
||||||
expectCalls: []string{"info"},
|
expectCalls: []string{"info"},
|
||||||
|
expectedCNICalls: []string{"GetContainerNetworkStatus"},
|
||||||
},
|
},
|
||||||
"sandbox status with arbitrary error": {
|
"sandbox status with arbitrary error": {
|
||||||
sandboxContainers: []container.Container{{
|
sandboxContainers: []container.Container{{
|
||||||
@ -157,16 +170,44 @@ func TestPodSandboxStatus(t *testing.T) {
|
|||||||
Pid: 1,
|
Pid: 1,
|
||||||
Status: container.Status_RUNNING,
|
Status: container.Status_RUNNING,
|
||||||
}},
|
}},
|
||||||
injectMetadata: true,
|
injectMetadata: true,
|
||||||
injectErr: errors.New("arbitrary error"),
|
expectState: runtime.PodSandboxState_SANDBOX_READY,
|
||||||
expectErr: true,
|
injectErr: errors.New("arbitrary error"),
|
||||||
expectCalls: []string{"info"},
|
expectErr: true,
|
||||||
|
expectCalls: []string{"info"},
|
||||||
|
expectedCNICalls: []string{},
|
||||||
|
},
|
||||||
|
"sandbox status with IP address": {
|
||||||
|
sandboxContainers: []container.Container{{
|
||||||
|
ID: sandboxStatusTestID,
|
||||||
|
Pid: 1,
|
||||||
|
Status: container.Status_RUNNING,
|
||||||
|
}},
|
||||||
|
injectMetadata: true,
|
||||||
|
expectState: runtime.PodSandboxState_SANDBOX_READY,
|
||||||
|
expectCalls: []string{"info"},
|
||||||
|
injectIP: true,
|
||||||
|
expectedCNICalls: []string{"GetContainerNetworkStatus"},
|
||||||
|
},
|
||||||
|
"sandbox status with GetContainerNetworkStatus returns error": {
|
||||||
|
sandboxContainers: []container.Container{{
|
||||||
|
ID: sandboxStatusTestID,
|
||||||
|
Pid: 1,
|
||||||
|
Status: container.Status_RUNNING,
|
||||||
|
}},
|
||||||
|
injectMetadata: 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)
|
t.Logf("TestCase %q", desc)
|
||||||
metadata, expect := getSandboxStatusTestData()
|
metadata, expect := getSandboxStatusTestData()
|
||||||
|
expect.Network.Ip = ""
|
||||||
c := newTestCRIContainerdService()
|
c := newTestCRIContainerdService()
|
||||||
fake := c.containerService.(*servertesting.FakeExecutionClient)
|
fake := c.containerService.(*servertesting.FakeExecutionClient)
|
||||||
|
fakeCNIPlugin := c.netPlugin.(*servertesting.FakeCNIPlugin)
|
||||||
fake.SetFakeContainers(test.sandboxContainers)
|
fake.SetFakeContainers(test.sandboxContainers)
|
||||||
if test.injectMetadata {
|
if test.injectMetadata {
|
||||||
assert.NoError(t, c.sandboxIDIndex.Add(metadata.ID))
|
assert.NoError(t, c.sandboxIDIndex.Add(metadata.ID))
|
||||||
@ -175,18 +216,31 @@ func TestPodSandboxStatus(t *testing.T) {
|
|||||||
if test.injectErr != nil {
|
if test.injectErr != nil {
|
||||||
fake.InjectError("info", test.injectErr)
|
fake.InjectError("info", test.injectErr)
|
||||||
}
|
}
|
||||||
|
if test.injectCNIErr != nil {
|
||||||
|
fakeCNIPlugin.InjectError("GetContainerNetworkStatus", test.injectCNIErr)
|
||||||
|
}
|
||||||
|
if test.injectIP {
|
||||||
|
fakeCNIPlugin.SetFakePodNetwork(metadata.NetNS, metadata.Config.GetMetadata().GetNamespace(),
|
||||||
|
metadata.Config.GetMetadata().GetName(), sandboxStatusTestID, sandboxStatusTestIP)
|
||||||
|
expect.Network.Ip = sandboxStatusTestIP
|
||||||
|
}
|
||||||
res, err := c.PodSandboxStatus(context.Background(), &runtime.PodSandboxStatusRequest{
|
res, err := c.PodSandboxStatus(context.Background(), &runtime.PodSandboxStatusRequest{
|
||||||
PodSandboxId: sandboxStatusTestID,
|
PodSandboxId: sandboxStatusTestID,
|
||||||
})
|
})
|
||||||
assert.Equal(t, test.expectCalls, fake.GetCalledNames())
|
assert.Equal(t, test.expectCalls, fake.GetCalledNames())
|
||||||
|
assert.Equal(t, test.expectedCNICalls, fakeCNIPlugin.GetCalledNames())
|
||||||
if test.expectErr {
|
if test.expectErr {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Nil(t, res)
|
assert.Nil(t, res)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
require.NotNil(t, res)
|
require.NotNil(t, res)
|
||||||
expect.State = test.expectState
|
expect.State = test.expectState
|
||||||
|
if expect.State == runtime.PodSandboxState_SANDBOX_NOTREADY {
|
||||||
|
expect.Linux.Namespaces.Network = ""
|
||||||
|
}
|
||||||
assert.Equal(t, expect, res.GetStatus())
|
assert.Equal(t, expect, res.GetStatus())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@ limitations under the License.
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -30,6 +32,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
"k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
||||||
|
|
||||||
"github.com/kubernetes-incubator/cri-containerd/pkg/metadata"
|
"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"
|
servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -38,6 +41,13 @@ func TestStopPodSandbox(t *testing.T) {
|
|||||||
testSandbox := metadata.SandboxMetadata{
|
testSandbox := metadata.SandboxMetadata{
|
||||||
ID: testID,
|
ID: testID,
|
||||||
Name: "test-name",
|
Name: "test-name",
|
||||||
|
Config: &runtime.PodSandboxConfig{
|
||||||
|
Metadata: &runtime.PodSandboxMetadata{
|
||||||
|
Name: "test-name",
|
||||||
|
Uid: "test-uid",
|
||||||
|
Namespace: "test-ns",
|
||||||
|
}},
|
||||||
|
NetNS: "test-netns",
|
||||||
}
|
}
|
||||||
testContainer := container.Container{
|
testContainer := container.Container{
|
||||||
ID: testID,
|
ID: testID,
|
||||||
@ -49,38 +59,71 @@ func TestStopPodSandbox(t *testing.T) {
|
|||||||
sandboxContainers []container.Container
|
sandboxContainers []container.Container
|
||||||
injectSandbox bool
|
injectSandbox bool
|
||||||
injectErr error
|
injectErr error
|
||||||
|
injectStatErr error
|
||||||
|
injectCNIErr error
|
||||||
expectErr bool
|
expectErr bool
|
||||||
expectCalls []string
|
expectCalls []string
|
||||||
|
expectedCNICalls []string
|
||||||
}{
|
}{
|
||||||
"stop non-existing sandbox": {
|
"stop non-existing sandbox": {
|
||||||
injectSandbox: false,
|
injectSandbox: false,
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
expectCalls: []string{},
|
expectCalls: []string{},
|
||||||
|
expectedCNICalls: []string{},
|
||||||
},
|
},
|
||||||
"stop sandbox with sandbox container": {
|
"stop sandbox with sandbox container": {
|
||||||
sandboxContainers: []container.Container{testContainer},
|
sandboxContainers: []container.Container{testContainer},
|
||||||
injectSandbox: true,
|
injectSandbox: true,
|
||||||
expectErr: false,
|
expectErr: false,
|
||||||
expectCalls: []string{"delete"},
|
expectCalls: []string{"delete"},
|
||||||
|
expectedCNICalls: []string{"TearDownPod"},
|
||||||
},
|
},
|
||||||
"stop sandbox with sandbox container not exist error": {
|
"stop sandbox with sandbox container not exist error": {
|
||||||
sandboxContainers: []container.Container{},
|
sandboxContainers: []container.Container{},
|
||||||
injectSandbox: true,
|
injectSandbox: true,
|
||||||
// Inject error to make sure fake execution client returns error.
|
// Inject error to make sure fake execution client returns error.
|
||||||
injectErr: grpc.Errorf(codes.Unknown, containerd.ErrContainerNotExist.Error()),
|
injectErr: grpc.Errorf(codes.Unknown, containerd.ErrContainerNotExist.Error()),
|
||||||
expectErr: false,
|
expectErr: false,
|
||||||
expectCalls: []string{"delete"},
|
expectCalls: []string{"delete"},
|
||||||
|
expectedCNICalls: []string{"TearDownPod"},
|
||||||
},
|
},
|
||||||
"stop sandbox with with arbitrary error": {
|
"stop sandbox with with arbitrary error": {
|
||||||
injectSandbox: true,
|
injectSandbox: true,
|
||||||
injectErr: grpc.Errorf(codes.Unknown, "arbitrary error"),
|
injectErr: grpc.Errorf(codes.Unknown, "arbitrary error"),
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
expectCalls: []string{"delete"},
|
expectCalls: []string{"delete"},
|
||||||
|
expectedCNICalls: []string{"TearDownPod"},
|
||||||
|
},
|
||||||
|
"stop sandbox with Stat returns arbitrary error": {
|
||||||
|
sandboxContainers: []container.Container{testContainer},
|
||||||
|
injectSandbox: true,
|
||||||
|
expectErr: true,
|
||||||
|
injectStatErr: errors.New("arbitrary error"),
|
||||||
|
expectCalls: []string{},
|
||||||
|
expectedCNICalls: []string{},
|
||||||
|
},
|
||||||
|
"stop sandbox with Stat returns not exist error": {
|
||||||
|
sandboxContainers: []container.Container{testContainer},
|
||||||
|
injectSandbox: true,
|
||||||
|
expectErr: false,
|
||||||
|
expectCalls: []string{"delete"},
|
||||||
|
injectStatErr: os.ErrNotExist,
|
||||||
|
expectedCNICalls: []string{},
|
||||||
|
},
|
||||||
|
"stop sandbox with TearDownPod fails": {
|
||||||
|
sandboxContainers: []container.Container{testContainer},
|
||||||
|
injectSandbox: true,
|
||||||
|
expectErr: true,
|
||||||
|
expectedCNICalls: []string{"TearDownPod"},
|
||||||
|
injectCNIErr: errors.New("arbitrary error"),
|
||||||
|
expectCalls: []string{},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Logf("TestCase %q", desc)
|
t.Logf("TestCase %q", desc)
|
||||||
c := newTestCRIContainerdService()
|
c := newTestCRIContainerdService()
|
||||||
fake := c.containerService.(*servertesting.FakeExecutionClient)
|
fake := c.containerService.(*servertesting.FakeExecutionClient)
|
||||||
|
fakeCNIPlugin := c.netPlugin.(*servertesting.FakeCNIPlugin)
|
||||||
|
fakeOS := c.os.(*ostesting.FakeOS)
|
||||||
fake.SetFakeContainers(test.sandboxContainers)
|
fake.SetFakeContainers(test.sandboxContainers)
|
||||||
|
|
||||||
if test.injectSandbox {
|
if test.injectSandbox {
|
||||||
@ -90,6 +133,14 @@ func TestStopPodSandbox(t *testing.T) {
|
|||||||
if test.injectErr != nil {
|
if test.injectErr != nil {
|
||||||
fake.InjectError("delete", test.injectErr)
|
fake.InjectError("delete", test.injectErr)
|
||||||
}
|
}
|
||||||
|
if test.injectCNIErr != nil {
|
||||||
|
fakeCNIPlugin.InjectError("TearDownPod", test.injectCNIErr)
|
||||||
|
}
|
||||||
|
if test.injectStatErr != nil {
|
||||||
|
fakeOS.InjectError("Stat", test.injectStatErr)
|
||||||
|
}
|
||||||
|
fakeCNIPlugin.SetFakePodNetwork(testSandbox.NetNS, testSandbox.Config.GetMetadata().GetNamespace(),
|
||||||
|
testSandbox.Config.GetMetadata().GetName(), testID, sandboxStatusTestIP)
|
||||||
|
|
||||||
res, err := c.StopPodSandbox(context.Background(), &runtime.StopPodSandboxRequest{
|
res, err := c.StopPodSandbox(context.Background(), &runtime.StopPodSandboxRequest{
|
||||||
PodSandboxId: testID,
|
PodSandboxId: testID,
|
||||||
@ -102,5 +153,6 @@ func TestStopPodSandbox(t *testing.T) {
|
|||||||
assert.NotNil(t, res)
|
assert.NotNil(t, res)
|
||||||
}
|
}
|
||||||
assert.Equal(t, test.expectCalls, fake.GetCalledNames())
|
assert.Equal(t, test.expectCalls, fake.GetCalledNames())
|
||||||
|
assert.Equal(t, test.expectedCNICalls, fakeCNIPlugin.GetCalledNames())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,7 @@ func newTestCRIContainerdService() *criContainerdService {
|
|||||||
sandboxIDIndex: truncindex.NewTruncIndex(nil),
|
sandboxIDIndex: truncindex.NewTruncIndex(nil),
|
||||||
containerStore: metadata.NewContainerStore(store.NewMetadataStore()),
|
containerStore: metadata.NewContainerStore(store.NewMetadataStore()),
|
||||||
containerNameIndex: registrar.NewRegistrar(),
|
containerNameIndex: registrar.NewRegistrar(),
|
||||||
|
netPlugin: servertesting.NewFakeCNIPlugin(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +66,7 @@ func TestSandboxOperations(t *testing.T) {
|
|||||||
c := newTestCRIContainerdService()
|
c := newTestCRIContainerdService()
|
||||||
fake := c.containerService.(*servertesting.FakeExecutionClient)
|
fake := c.containerService.(*servertesting.FakeExecutionClient)
|
||||||
fakeOS := c.os.(*ostesting.FakeOS)
|
fakeOS := c.os.(*ostesting.FakeOS)
|
||||||
|
fakeCNIPlugin := c.netPlugin.(*servertesting.FakeCNIPlugin)
|
||||||
fakeOS.OpenFifoFn = func(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
|
fakeOS.OpenFifoFn = func(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
|
||||||
return nopReadWriteCloser{}, nil
|
return nopReadWriteCloser{}, nil
|
||||||
}
|
}
|
||||||
@ -89,6 +91,7 @@ func TestSandboxOperations(t *testing.T) {
|
|||||||
|
|
||||||
t.Logf("should be able to get pod sandbox status")
|
t.Logf("should be able to get pod sandbox status")
|
||||||
info, err := fake.Info(context.Background(), &execution.InfoRequest{ID: id})
|
info, err := fake.Info(context.Background(), &execution.InfoRequest{ID: id})
|
||||||
|
netns := getNetworkNamespace(info.Pid)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
expectSandboxStatus := &runtime.PodSandboxStatus{
|
expectSandboxStatus := &runtime.PodSandboxStatus{
|
||||||
Id: id,
|
Id: id,
|
||||||
@ -97,7 +100,7 @@ func TestSandboxOperations(t *testing.T) {
|
|||||||
Network: &runtime.PodSandboxNetworkStatus{},
|
Network: &runtime.PodSandboxNetworkStatus{},
|
||||||
Linux: &runtime.LinuxPodSandboxStatus{
|
Linux: &runtime.LinuxPodSandboxStatus{
|
||||||
Namespaces: &runtime.Namespace{
|
Namespaces: &runtime.Namespace{
|
||||||
Network: getNetworkNamespace(info.Pid),
|
Network: netns,
|
||||||
Options: &runtime.NamespaceOption{
|
Options: &runtime.NamespaceOption{
|
||||||
HostNetwork: false,
|
HostNetwork: false,
|
||||||
HostPid: false,
|
HostPid: false,
|
||||||
@ -113,6 +116,9 @@ func TestSandboxOperations(t *testing.T) {
|
|||||||
require.NotNil(t, statusRes)
|
require.NotNil(t, statusRes)
|
||||||
status := statusRes.GetStatus()
|
status := statusRes.GetStatus()
|
||||||
expectSandboxStatus.CreatedAt = status.GetCreatedAt()
|
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)
|
assert.Equal(t, expectSandboxStatus, status)
|
||||||
|
|
||||||
t.Logf("should be able to list pod sandboxes")
|
t.Logf("should be able to list pod sandboxes")
|
||||||
|
181
pkg/server/testing/fake_cni_plugin.go
Normal file
181
pkg/server/testing/fake_cni_plugin.go
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/kubernetes-incubator/cri-o/pkg/ocicni"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CNIPluginArgument is arguments used to call CNI related functions.
|
||||||
|
type CNIPluginArgument struct {
|
||||||
|
NetnsPath string
|
||||||
|
Namespace string
|
||||||
|
Name string
|
||||||
|
ContainerID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// FakeCNIPlugin is a fake plugin used for test.
|
||||||
|
type FakeCNIPlugin struct {
|
||||||
|
sync.Mutex
|
||||||
|
called []CalledDetail
|
||||||
|
errors map[string]error
|
||||||
|
IPMap map[CNIPluginArgument]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// getError get error for call
|
||||||
|
func (f *FakeCNIPlugin) 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 *FakeCNIPlugin) InjectError(fn string, err error) {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
f.errors[fn] = err
|
||||||
|
}
|
||||||
|
|
||||||
|
// InjectErrors inject errors for calls
|
||||||
|
func (f *FakeCNIPlugin) 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 *FakeCNIPlugin) ClearErrors() {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
f.errors = make(map[string]error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FakeCNIPlugin) appendCalled(name string, argument interface{}) {
|
||||||
|
call := CalledDetail{Name: name, Argument: argument}
|
||||||
|
f.called = append(f.called, call)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCalledNames get names of call
|
||||||
|
func (f *FakeCNIPlugin) 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 *FakeCNIPlugin) GetCalledDetails() []CalledDetail {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
// Copy the list and return.
|
||||||
|
return append([]CalledDetail{}, f.called...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFakePodNetwork sets the given IP for given arguments.
|
||||||
|
func (f *FakeCNIPlugin) SetFakePodNetwork(netnsPath string, namespace string, name string, containerID string, ip string) {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
arg := CNIPluginArgument{netnsPath, namespace, name, containerID}
|
||||||
|
f.IPMap[arg] = ip
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFakeCNIPlugin create a FakeCNIPlugin.
|
||||||
|
func NewFakeCNIPlugin() ocicni.CNIPlugin {
|
||||||
|
return &FakeCNIPlugin{
|
||||||
|
errors: make(map[string]error),
|
||||||
|
IPMap: make(map[CNIPluginArgument]string),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name return the name of fake CNI plugin.
|
||||||
|
func (f *FakeCNIPlugin) Name() string {
|
||||||
|
return "fake-CNI-plugin"
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUpPod setup the network of PodSandbox.
|
||||||
|
func (f *FakeCNIPlugin) SetUpPod(netnsPath string, namespace string, name string, containerID string) error {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
arg := CNIPluginArgument{netnsPath, namespace, name, containerID}
|
||||||
|
f.appendCalled("SetUpPod", arg)
|
||||||
|
if err := f.getError("SetUpPod"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.IPMap[arg] = generateIP()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TearDownPod teardown the network of PodSandbox.
|
||||||
|
func (f *FakeCNIPlugin) TearDownPod(netnsPath string, namespace string, name string, containerID string) error {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
arg := CNIPluginArgument{netnsPath, namespace, name, containerID}
|
||||||
|
f.appendCalled("TearDownPod", arg)
|
||||||
|
if err := f.getError("TearDownPod"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, ok := f.IPMap[arg]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("failed to find the IP")
|
||||||
|
}
|
||||||
|
delete(f.IPMap, arg)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContainerNetworkStatus get the status of network.
|
||||||
|
func (f *FakeCNIPlugin) GetContainerNetworkStatus(netnsPath string, namespace string, name string, containerID string) (string, error) {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
arg := CNIPluginArgument{netnsPath, namespace, name, containerID}
|
||||||
|
f.appendCalled("GetContainerNetworkStatus", arg)
|
||||||
|
if err := f.getError("GetContainerNetworkStatus"); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
ip, ok := f.IPMap[arg]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("failed to find the IP")
|
||||||
|
}
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status get the status of the plugin.
|
||||||
|
func (f *FakeCNIPlugin) Status() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIP() string {
|
||||||
|
rand.Seed(time.Now().Unix())
|
||||||
|
p1 := strconv.Itoa(rand.Intn(266))
|
||||||
|
p2 := strconv.Itoa(rand.Intn(266))
|
||||||
|
p3 := strconv.Itoa(rand.Intn(266))
|
||||||
|
p4 := strconv.Itoa(rand.Intn(266))
|
||||||
|
return p1 + "." + p2 + "." + p3 + "." + p4
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user