2018-03-27 10:03:53 -04:00
|
|
|
package containerd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"sync"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/docker/docker/libcontainerd"
|
2018-05-19 07:38:54 -04:00
|
|
|
"github.com/opencontainers/runtime-spec/specs-go"
|
2018-03-27 10:03:53 -04:00
|
|
|
"github.com/pkg/errors"
|
2018-06-11 09:32:11 -04:00
|
|
|
"gotest.tools/assert"
|
2018-03-27 10:03:53 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestLifeCycle(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
mock := newMockClient()
|
|
|
|
exec, cleanup := setupTest(t, mock, mock)
|
|
|
|
defer cleanup()
|
|
|
|
|
|
|
|
id := "test-create"
|
|
|
|
mock.simulateStartError(true, id)
|
|
|
|
err := exec.Create(id, specs.Spec{}, nil, nil)
|
|
|
|
assert.Assert(t, err != nil)
|
|
|
|
mock.simulateStartError(false, id)
|
|
|
|
|
|
|
|
err = exec.Create(id, specs.Spec{}, nil, nil)
|
2019-01-21 07:16:02 -05:00
|
|
|
assert.NilError(t, err)
|
2018-03-27 10:03:53 -04:00
|
|
|
running, _ := exec.IsRunning(id)
|
|
|
|
assert.Assert(t, running)
|
|
|
|
|
|
|
|
// create with the same ID
|
|
|
|
err = exec.Create(id, specs.Spec{}, nil, nil)
|
|
|
|
assert.Assert(t, err != nil)
|
|
|
|
|
|
|
|
mock.HandleExitEvent(id) // simulate a plugin that exits
|
|
|
|
|
|
|
|
err = exec.Create(id, specs.Spec{}, nil, nil)
|
2019-01-21 07:16:02 -05:00
|
|
|
assert.NilError(t, err)
|
2018-03-27 10:03:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func setupTest(t *testing.T, client Client, eh ExitHandler) (*Executor, func()) {
|
|
|
|
rootDir, err := ioutil.TempDir("", "test-daemon")
|
2019-01-21 07:16:02 -05:00
|
|
|
assert.NilError(t, err)
|
2018-03-27 10:03:53 -04:00
|
|
|
assert.Assert(t, client != nil)
|
|
|
|
assert.Assert(t, eh != nil)
|
|
|
|
|
|
|
|
return &Executor{
|
|
|
|
rootDir: rootDir,
|
|
|
|
client: client,
|
|
|
|
exitHandler: eh,
|
|
|
|
}, func() {
|
|
|
|
assert.Assert(t, os.RemoveAll(rootDir))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type mockClient struct {
|
|
|
|
mu sync.Mutex
|
|
|
|
containers map[string]bool
|
|
|
|
errorOnStart map[string]bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func newMockClient() *mockClient {
|
|
|
|
return &mockClient{
|
|
|
|
containers: make(map[string]bool),
|
|
|
|
errorOnStart: make(map[string]bool),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockClient) Create(ctx context.Context, id string, _ *specs.Spec, _ interface{}) error {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
if _, ok := c.containers[id]; ok {
|
|
|
|
return errors.New("exists")
|
|
|
|
}
|
|
|
|
|
|
|
|
c.containers[id] = false
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockClient) Restore(ctx context.Context, id string, attachStdio libcontainerd.StdioCallback) (alive bool, pid int, err error) {
|
|
|
|
return false, 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockClient) Status(ctx context.Context, id string) (libcontainerd.Status, error) {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
running, ok := c.containers[id]
|
|
|
|
if !ok {
|
|
|
|
return libcontainerd.StatusUnknown, errors.New("not found")
|
|
|
|
}
|
|
|
|
if running {
|
|
|
|
return libcontainerd.StatusRunning, nil
|
|
|
|
}
|
|
|
|
return libcontainerd.StatusStopped, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockClient) Delete(ctx context.Context, id string) error {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
delete(c.containers, id)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockClient) DeleteTask(ctx context.Context, id string) (uint32, time.Time, error) {
|
|
|
|
return 0, time.Time{}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockClient) Start(ctx context.Context, id, checkpointDir string, withStdin bool, attachStdio libcontainerd.StdioCallback) (pid int, err error) {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
if _, ok := c.containers[id]; !ok {
|
|
|
|
return 0, errors.New("not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.errorOnStart[id] {
|
|
|
|
return 0, errors.New("some startup error")
|
|
|
|
}
|
|
|
|
c.containers[id] = true
|
|
|
|
return 1, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockClient) SignalProcess(ctx context.Context, containerID, processID string, signal int) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockClient) simulateStartError(sim bool, id string) {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
if sim {
|
|
|
|
c.errorOnStart[id] = sim
|
|
|
|
return
|
|
|
|
}
|
|
|
|
delete(c.errorOnStart, id)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockClient) HandleExitEvent(id string) error {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
delete(c.containers, id)
|
|
|
|
return nil
|
|
|
|
}
|