Skip to content

Commit

Permalink
Merge pull request kata-containers#207 from devimc/reaper/unitTest
Browse files Browse the repository at this point in the history
implement mockreaper for unit testing
  • Loading branch information
Sebastien Boeuf authored Apr 6, 2018
2 parents c775672 + ee7850d commit a80ad16
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 13 deletions.
9 changes: 5 additions & 4 deletions agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ type sandbox struct {
grpcListener net.Listener
sharedPidNs namespace
mounts []string
subreaper *reaper
subreaper reaper
}

type namespace struct {
Expand Down Expand Up @@ -575,16 +575,17 @@ func main() {
os.Exit(exitSuccess)
}()

r := &agentReaper{}
r.init()

// Initialize unique sandbox structure.
s := &sandbox{
containers: make(map[string]*container),
running: false,
// pivot_root won't work for init, see
// Documention/filesystem/ramfs-rootfs-initramfs.txt
noPivotRoot: os.Getpid() == 1,
subreaper: &reaper{
exitCodeChans: make(map[int]chan<- int),
},
subreaper: r,
}

if err = s.initLogger(); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,8 @@ func (a *agentGRPC) execProcess(ctr *container, proc *process, createContainer b
// miss the opportunity to get the exit code, leading WaitProcess() to
// wait forever on the new channel.
// This lock has to be taken before we run the new process.
a.sandbox.subreaper.RLock()
defer a.sandbox.subreaper.RUnlock()
a.sandbox.subreaper.lock()
defer a.sandbox.subreaper.unlock()

if createContainer {
err = ctr.container.Start(&proc.process)
Expand Down
43 changes: 43 additions & 0 deletions mockreaper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// Copyright (c) 2018 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//

package main

import "os/exec"

type mockreaper struct {
}

func (r *mockreaper) init() {
}

func (r *mockreaper) getExitCodeCh(pid int) (chan<- int, error) {
return nil, nil
}

func (r *mockreaper) setExitCodeCh(pid int, exitCodeCh chan<- int) {
}

func (r *mockreaper) deleteExitCodeCh(pid int) {
}

func (r *mockreaper) reap() error {
return nil
}

func (r *mockreaper) start(c *exec.Cmd) (<-chan int, error) {
return nil, nil
}

func (r *mockreaper) wait(exitCodeCh <-chan int, proc waitProcess) (int, error) {
return 0, nil
}

func (r *mockreaper) lock() {
}

func (r *mockreaper) unlock() {
}
69 changes: 69 additions & 0 deletions mockreaper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//
// Copyright (c) 2018 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//

package main

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestMockReaperInit(t *testing.T) {
m := &mockreaper{}
m.init()
}

func TestMockReaperGetExitCodeCh(t *testing.T) {
assert := assert.New(t)
m := &mockreaper{}
c, e := m.getExitCodeCh(0)
assert.Nil(c)
assert.NoError(e)
}

func TestMockReaperSetExitCodeCh(t *testing.T) {
m := &mockreaper{}
m.setExitCodeCh(0, nil)
}

func TestMockReaperDeleteExitCodeCh(t *testing.T) {
m := &mockreaper{}
m.deleteExitCodeCh(0)
}

func TestMockReaperReap(t *testing.T) {
assert := assert.New(t)
m := &mockreaper{}
err := m.reap()
assert.NoError(err)
}

func TestMockReaperStart(t *testing.T) {
assert := assert.New(t)
m := &mockreaper{}
c, e := m.start(nil)
assert.Nil(c)
assert.NoError(e)
}

func TestMockReaperWait(t *testing.T) {
assert := assert.New(t)
m := &mockreaper{}
e, err := m.wait(nil, &reaperOSProcess{})
assert.Equal(0, e)
assert.NoError(err)
}

func TestMockReaperLock(t *testing.T) {
m := &mockreaper{}
m.lock()
}

func TestMockReaperUnlock(t *testing.T) {
m := &mockreaper{}
m.unlock()
}
38 changes: 31 additions & 7 deletions reaper.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,19 @@ import (
grpcStatus "google.golang.org/grpc/status"
)

type reaper struct {
type reaper interface {
init()
getExitCodeCh(pid int) (chan<- int, error)
setExitCodeCh(pid int, exitCodeCh chan<- int)
deleteExitCodeCh(pid int)
reap() error
start(c *exec.Cmd) (<-chan int, error)
wait(exitCodeCh <-chan int, proc waitProcess) (int, error)
lock()
unlock()
}

type agentReaper struct {
sync.RWMutex

chansLock sync.RWMutex
Expand All @@ -33,7 +45,19 @@ func exitStatus(status unix.WaitStatus) int {
return status.ExitStatus()
}

func (r *reaper) getExitCodeCh(pid int) (chan<- int, error) {
func (r *agentReaper) init() {
r.exitCodeChans = make(map[int]chan<- int)
}

func (r *agentReaper) lock() {
r.RLock()
}

func (r *agentReaper) unlock() {
r.RUnlock()
}

func (r *agentReaper) getExitCodeCh(pid int) (chan<- int, error) {
r.chansLock.RLock()
defer r.chansLock.RUnlock()

Expand All @@ -45,21 +69,21 @@ func (r *reaper) getExitCodeCh(pid int) (chan<- int, error) {
return exitCodeCh, nil
}

func (r *reaper) setExitCodeCh(pid int, exitCodeCh chan<- int) {
func (r *agentReaper) setExitCodeCh(pid int, exitCodeCh chan<- int) {
r.chansLock.Lock()
defer r.chansLock.Unlock()

r.exitCodeChans[pid] = exitCodeCh
}

func (r *reaper) deleteExitCodeCh(pid int) {
func (r *agentReaper) deleteExitCodeCh(pid int) {
r.chansLock.Lock()
defer r.chansLock.Unlock()

delete(r.exitCodeChans, pid)
}

func (r *reaper) reap() error {
func (r *agentReaper) reap() error {
var (
ws unix.WaitStatus
rus unix.Rusage
Expand Down Expand Up @@ -119,7 +143,7 @@ func (r *reaper) reap() error {
// start starts the exec command and registers the process to the reaper.
// This function is a helper for exec.Cmd.Start() since this needs to be
// in sync with exec.Cmd.Wait().
func (r *reaper) start(c *exec.Cmd) (<-chan int, error) {
func (r *agentReaper) start(c *exec.Cmd) (<-chan int, error) {
// This lock is very important to avoid any race with reaper.reap().
// We don't want the reaper to reap a process before we have added
// it to the exit code channel list.
Expand All @@ -143,7 +167,7 @@ func (r *reaper) start(c *exec.Cmd) (<-chan int, error) {
// from the subreaper, the exit code is sent through the provided channel.
// This function is a helper for exec.Cmd.Wait() and os.Process.Wait() since
// both cannot be used directly, because of the subreaper.
func (r *reaper) wait(exitCodeCh <-chan int, proc waitProcess) (int, error) {
func (r *agentReaper) wait(exitCodeCh <-chan int, proc waitProcess) (int, error) {
// Wait for the subreaper to receive the SIGCHLD signal. Once it gets
// it, this channel will be notified by receiving the exit code of the
// corresponding process.
Expand Down

0 comments on commit a80ad16

Please sign in to comment.