| // 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", |
| }} |
| |
| config := &Config{ |
| Memory: 4, |
| Cpus: 4, |
| NetworkMode: "host", |
| } |
| |
| 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, config, 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, config, 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) |
| } |
| }) |
| } |