Skip to content

Commit

Permalink
Merge pull request #23 from heartlock/fake-execution-service
Browse files Browse the repository at this point in the history
Add fake execution service
  • Loading branch information
Random-Liu committed May 4, 2017
2 parents 0fa4e74 + e3565bb commit 4c86ac9
Show file tree
Hide file tree
Showing 232 changed files with 126,087 additions and 0 deletions.
14 changes: 14 additions & 0 deletions Godeps/Godeps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

317 changes: 317 additions & 0 deletions pkg/server/testing/fake_execution_client.go
@@ -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
}
3 changes: 3 additions & 0 deletions vendor/github.com/containerd/containerd/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions vendor/github.com/containerd/containerd/.travis.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 4c86ac9

Please sign in to comment.