Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #23 from heartlock/fake-execution-service
Add fake execution service
- Loading branch information
Showing
232 changed files
with
126,087 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,317 @@ | ||
/* | ||
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" | ||
"github.com/containerd/containerd/api/services/execution" | ||
"github.com/containerd/containerd/api/types/container" | ||
google_protobuf "github.com/golang/protobuf/ptypes/empty" | ||
"golang.org/x/net/context" | ||
"google.golang.org/grpc" | ||
) | ||
|
||
type calledDetail struct { | ||
name string | ||
argument interface{} | ||
} | ||
|
||
var _ execution.ContainerService_EventsClient = &EventClient{} | ||
|
||
// EventClient is a test implementation of execution.ContainerService_EventsClient | ||
type EventClient struct { | ||
Events chan *container.Event | ||
grpc.ClientStream | ||
} | ||
|
||
// Recv is a test implementation of Recv | ||
func (cli *EventClient) Recv() (*container.Event, error) { | ||
event := <-cli.Events | ||
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 | ||
ContainerList map[string]container.Container | ||
eventsQueue chan *container.Event | ||
eventClients []*EventClient | ||
} | ||
|
||
var _ execution.ContainerServiceClient = &FakeExecutionClient{} | ||
|
||
// NewFakeExecutionClient creates a FakeExecutionClient | ||
func NewFakeExecutionClient() *FakeExecutionClient { | ||
return &FakeExecutionClient{ | ||
errors: make(map[string]error), | ||
ContainerList: make(map[string]container.Container), | ||
} | ||
} | ||
|
||
// WithEvents setup events publisher for FakeExecutionClient | ||
func (f *FakeExecutionClient) WithEvents() *FakeExecutionClient { | ||
f.eventsQueue = make(chan *container.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) popError(op string) error { | ||
if f.errors == nil { | ||
return nil | ||
} | ||
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 *container.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 | ||
} | ||
|
||
// 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.popError("create"); err != nil { | ||
return nil, err | ||
} | ||
_, ok := f.ContainerList[createOpts.ID] | ||
if ok { | ||
return nil, containerd.ErrContainerExists | ||
} | ||
pid := generatePid() | ||
f.ContainerList[createOpts.ID] = container.Container{ | ||
ID: createOpts.ID, | ||
Pid: pid, | ||
Status: container.Status_CREATED, | ||
} | ||
f.sendEvent(&container.Event{ | ||
ID: createOpts.ID, | ||
Type: container.Event_CREATE, | ||
Pid: pid, | ||
}) | ||
return &execution.CreateResponse{ | ||
ID: createOpts.ID, | ||
Pid: pid, | ||
}, nil | ||
} | ||
|
||
// Start is a test implementation of execution.Start | ||
func (f *FakeExecutionClient) Start(ctx context.Context, startOpts *execution.StartRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) { | ||
f.Lock() | ||
defer f.Unlock() | ||
f.appendCalled("start", startOpts) | ||
if err := f.popError("start"); err != nil { | ||
return nil, err | ||
} | ||
c, ok := f.ContainerList[startOpts.ID] | ||
if !ok { | ||
return nil, containerd.ErrContainerNotExist | ||
} | ||
f.sendEvent(&container.Event{ | ||
ID: c.ID, | ||
Type: container.Event_START, | ||
Pid: c.Pid, | ||
}) | ||
switch c.Status { | ||
case container.Status_CREATED: | ||
c.Status = container.Status_RUNNING | ||
return &google_protobuf.Empty{}, nil | ||
case container.Status_STOPPED: | ||
return &google_protobuf.Empty{}, fmt.Errorf("cannot start a container that has stopped") | ||
case container.Status_RUNNING: | ||
return &google_protobuf.Empty{}, fmt.Errorf("cannot start an already running container") | ||
default: | ||
return &google_protobuf.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.popError("delete"); err != nil { | ||
return nil, err | ||
} | ||
c, ok := f.ContainerList[deleteOpts.ID] | ||
if !ok { | ||
return nil, containerd.ErrContainerNotExist | ||
} | ||
delete(f.ContainerList, deleteOpts.ID) | ||
f.sendEvent(&container.Event{ | ||
ID: c.ID, | ||
Type: container.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) (*container.Container, error) { | ||
f.Lock() | ||
defer f.Unlock() | ||
f.appendCalled("info", infoOpts) | ||
if err := f.popError("info"); err != nil { | ||
return nil, err | ||
} | ||
c, ok := f.ContainerList[infoOpts.ID] | ||
if !ok { | ||
return nil, containerd.ErrContainerNotExist | ||
} | ||
return &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.popError("list"); err != nil { | ||
return nil, err | ||
} | ||
resp := &execution.ListResponse{} | ||
for _, c := range f.ContainerList { | ||
resp.Containers = append(resp.Containers, &container.Container{ | ||
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) (*google_protobuf.Empty, error) { | ||
f.Lock() | ||
defer f.Unlock() | ||
f.appendCalled("kill", killOpts) | ||
if err := f.popError("kill"); err != nil { | ||
return nil, err | ||
} | ||
c, ok := f.ContainerList[killOpts.ID] | ||
if !ok { | ||
return nil, containerd.ErrContainerNotExist | ||
} | ||
c.Status = container.Status_STOPPED | ||
f.sendEvent(&container.Event{ | ||
ID: c.ID, | ||
Type: container.Event_EXIT, | ||
Pid: c.Pid, | ||
}) | ||
return &google_protobuf.Empty{}, nil | ||
} | ||
|
||
// Events is a test implementation of execution.Events | ||
func (f *FakeExecutionClient) Events(ctx context.Context, eventsOpts *execution.EventsRequest, opts ...grpc.CallOption) (execution.ContainerService_EventsClient, error) { | ||
f.Lock() | ||
defer f.Unlock() | ||
f.appendCalled("events", eventsOpts) | ||
if err := f.popError("events"); err != nil { | ||
return nil, err | ||
} | ||
var client = &EventClient{ | ||
Events: make(chan *container.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) (*google_protobuf.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) (*google_protobuf.Empty, error) { | ||
// TODO: implement CloseStdin() | ||
return nil, nil | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.