blob: 77d6ccbe70e527122806121271f48d364910766c [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"bytes"
"context"
"errors"
"io"
"io/ioutil"
"testing"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
)
// mockedCommonAPIClient provides mock implementations of Docker client methods needed by Pool.
type mockedCommonAPIClient struct {
client.CommonAPIClient
containers map[string]bool
messages chan events.Message
errors chan error
}
func (m *mockedCommonAPIClient) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (container.ContainerCreateCreatedBody, error) {
return container.ContainerCreateCreatedBody{ID: "test"}, nil
}
func (m *mockedCommonAPIClient) ContainerStart(ctx context.Context, containerID string, options types.ContainerStartOptions) error {
if containerID != "test" {
return errors.New("invalid ID")
}
return nil
}
func (m *mockedCommonAPIClient) Events(ctx context.Context, options types.EventsOptions) (<-chan events.Message, <-chan error) {
return m.messages, m.errors
}
func (m *mockedCommonAPIClient) ContainerRestart(ctx context.Context, containerID string, timeout *time.Duration) error {
if containerID != "test" {
return errors.New("invalid ID")
}
return nil
}
func (m *mockedCommonAPIClient) ContainerStop(ctx context.Context, containerID string, timeout *time.Duration) error {
if containerID != "test" {
return errors.New("invalid ID")
}
return nil
}
func (m *mockedCommonAPIClient) ContainerWait(ctx context.Context, containerID string, condition container.WaitCondition) (<-chan container.ContainerWaitOKBody, <-chan error) {
msgs := make(chan container.ContainerWaitOKBody, 1)
msgs <- container.ContainerWaitOKBody{StatusCode: 0}
errs := make(chan error, 0)
return msgs, errs
}
func (m *mockedCommonAPIClient) ContainerRemove(ctx context.Context, containerID string, options types.ContainerRemoveOptions) error {
if containerID != "test" {
return errors.New("invalid ID")
}
return nil
}
func (m *mockedCommonAPIClient) ContainerKill(ctx context.Context, container, signal string) error {
return nil
}
// mockedImageAPIClient provides mock implementations of Docker client methods needed by imagePull.
type mockedImageAPIClient struct {
client.ImageAPIClient
images map[string]types.ImageSummary
}
func (m *mockedImageAPIClient) ImageList(ctx context.Context, options types.ImageListOptions) ([]types.ImageSummary, error) {
reference := options.Filters.Get("reference")
images := []types.ImageSummary{}
for _, r := range reference {
if image, ok := m.images[r]; ok {
images = append(images, image)
}
}
return images, nil
}
func (m *mockedImageAPIClient) ImagePull(ctx context.Context, ref string, options types.ImagePullOptions) (io.ReadCloser, error) {
if _, ok := m.images[ref]; !ok {
return nil, errors.New("image does not exist")
}
return ioutil.NopCloser(bytes.NewReader([]byte(`{"status": "done"}`))), nil
}
func TestImage(t *testing.T) {
ctx := context.Background()
mockClient := mockedImageAPIClient{
images: map[string]types.ImageSummary{
registryDomain + "/foo/bar": types.ImageSummary{},
},
}
t.Run("image exists", func(t *testing.T) {
image := NewImage(&mockClient)
if err := image.Pull(ctx, "foo", "bar", "key"); err != nil {
t.Error("image pull failed", err)
}
})
t.Run("image does not exists", func(t *testing.T) {
mockClient := mockedImageAPIClient{}
image := NewImage(&mockClient)
if err := image.Pull(ctx, "foo", "baz", "key"); err == nil {
t.Error("image pull didn't fail", err)
}
})
}
func TestPool(t *testing.T) {
ctx := context.Background()
// TODO: test behavior with multiple containers
containers := []*Container{&Container{
Name: "test",
hostname: "test--test001",
domainname: "local",
workdir: "/b/test001",
cpuset: "0-1",
}}
t.Run("pool create, restart and remove", func(t *testing.T) {
msgs := make(chan events.Message, 1)
errs := make(chan error, 1)
mockClient := mockedCommonAPIClient{
containers: map[string]bool{
"test--test001": true,
},
messages: msgs,
errors: errs,
}
pool := NewPool(&mockClient)
if err := <-pool.Create(ctx, containers); err != nil {
t.Error("pool creation failed", err)
}
pool.Serve(ctx)
mockClient.messages <- events.Message{
Action: "die",
Actor: events.Actor{
Attributes: map[string]string{
"name": "test",
},
},
}
for err := range pool.Remove(ctx) {
t.Error("pool removal failed", err)
}
})
t.Run("pool create, drain and remove", func(t *testing.T) {
mockClient := mockedCommonAPIClient{
containers: map[string]bool{
"test--test001": true,
},
}
pool := NewPool(&mockClient)
if err := <-pool.Create(ctx, containers); err != nil {
t.Error("pool creation failed", err)
}
pool.Serve(ctx)
for err := range pool.Drain(ctx) {
t.Error("pool drain failed", err)
}
for err := range pool.Remove(ctx) {
t.Error("pool removal failed", err)
}
})
}