From 8c8987b666ac6fa888e49f2b6aa7743b6178133d Mon Sep 17 00:00:00 2001 From: Amir Raminfar Date: Tue, 11 Jul 2023 13:21:46 -0700 Subject: [PATCH] chore(refactor): refactors docker client for better testing (#2302) * chore(refactor): refactors docker client for better testing * more refactoring and clenaing up tests --- docker/client.go | 50 +++++++---------- docker/client_test.go | 16 +++--- docker/event_generator.go | 36 ------------ docker/level_guesser.go | 41 ++++++++++++++ main.go | 10 ++-- main_test.go | 115 ++++++++++++++++++-------------------- web/events.go | 4 +- web/routes.go | 26 +++++++-- web/routes_test.go | 8 +-- 9 files changed, 157 insertions(+), 149 deletions(-) create mode 100644 docker/level_guesser.go diff --git a/docker/client.go b/docker/client.go index 965f6e82..74d0b5e2 100644 --- a/docker/client.go +++ b/docker/client.go @@ -20,12 +20,6 @@ import ( log "github.com/sirupsen/logrus" ) -type dockerClient struct { - cli dockerProxy - filters filters.Args - host *Host -} - type StdType int const ( @@ -48,7 +42,7 @@ func (s StdType) String() string { } } -type dockerProxy interface { +type DockerCLI interface { ContainerList(context.Context, types.ContainerListOptions) ([]types.Container, error) ContainerLogs(context.Context, string, types.ContainerLogsOptions) (io.ReadCloser, error) Events(context.Context, types.EventsOptions) (<-chan events.Message, <-chan error) @@ -57,20 +51,18 @@ type dockerProxy interface { Ping(ctx context.Context) (types.Ping, error) } -// Client is a proxy around the docker client -type Client interface { - ListContainers() ([]Container, error) - FindContainer(string) (Container, error) - ContainerLogs(context.Context, string, string, StdType) (io.ReadCloser, error) - Events(context.Context, chan<- ContainerEvent) <-chan error - ContainerLogsBetweenDates(context.Context, string, time.Time, time.Time, StdType) (io.ReadCloser, error) - ContainerStats(context.Context, string, chan<- ContainerStat) error - Ping(context.Context) (types.Ping, error) - Host() *Host +type Client struct { + cli DockerCLI + filters filters.Args + host *Host +} + +func NewClient(cli DockerCLI, filters filters.Args, host *Host) *Client { + return &Client{cli, filters, host} } // NewClientWithFilters creates a new instance of Client with docker filters -func NewClientWithFilters(f map[string][]string) (Client, error) { +func NewClientWithFilters(f map[string][]string) (*Client, error) { filterArgs := filters.NewArgs() for key, values := range f { for _, value := range values { @@ -86,10 +78,10 @@ func NewClientWithFilters(f map[string][]string) (Client, error) { return nil, err } - return &dockerClient{cli, filterArgs, &Host{Name: "localhost", ID: "localhost"}}, nil + return NewClient(cli, filterArgs, &Host{Name: "localhost", ID: "localhost"}), nil } -func NewClientWithTlsAndFilter(f map[string][]string, host Host) (Client, error) { +func NewClientWithTlsAndFilter(f map[string][]string, host Host) (*Client, error) { filterArgs := filters.NewArgs() for key, values := range f { for _, value := range values { @@ -122,10 +114,10 @@ func NewClientWithTlsAndFilter(f map[string][]string, host Host) (Client, error) return nil, err } - return &dockerClient{cli, filterArgs, &host}, nil + return NewClient(cli, filterArgs, &host), nil } -func (d *dockerClient) FindContainer(id string) (Container, error) { +func (d *Client) FindContainer(id string) (Container, error) { var container Container containers, err := d.ListContainers() if err != nil { @@ -153,7 +145,7 @@ func (d *dockerClient) FindContainer(id string) (Container, error) { return container, nil } -func (d *dockerClient) ListContainers() ([]Container, error) { +func (d *Client) ListContainers() ([]Container, error) { containerListOptions := types.ContainerListOptions{ Filters: d.filters, All: true, @@ -188,7 +180,7 @@ func (d *dockerClient) ListContainers() ([]Container, error) { return containers, nil } -func (d *dockerClient) ContainerStats(ctx context.Context, id string, stats chan<- ContainerStat) error { +func (d *Client) ContainerStats(ctx context.Context, id string, stats chan<- ContainerStat) error { response, err := d.cli.ContainerStats(ctx, id, true) if err != nil { @@ -240,7 +232,7 @@ func (d *dockerClient) ContainerStats(ctx context.Context, id string, stats chan return nil } -func (d *dockerClient) ContainerLogs(ctx context.Context, id string, since string, stdType StdType) (io.ReadCloser, error) { +func (d *Client) ContainerLogs(ctx context.Context, id string, since string, stdType StdType) (io.ReadCloser, error) { log.WithField("id", id).WithField("since", since).WithField("stdType", stdType).Debug("streaming logs for container") if since != "" { @@ -268,7 +260,7 @@ func (d *dockerClient) ContainerLogs(ctx context.Context, id string, since strin return reader, nil } -func (d *dockerClient) Events(ctx context.Context, messages chan<- ContainerEvent) <-chan error { +func (d *Client) Events(ctx context.Context, messages chan<- ContainerEvent) <-chan error { dockerMessages, errors := d.cli.Events(ctx, types.EventsOptions{}) go func() { @@ -296,7 +288,7 @@ func (d *dockerClient) Events(ctx context.Context, messages chan<- ContainerEven return errors } -func (d *dockerClient) ContainerLogsBetweenDates(ctx context.Context, id string, from time.Time, to time.Time, stdType StdType) (io.ReadCloser, error) { +func (d *Client) ContainerLogsBetweenDates(ctx context.Context, id string, from time.Time, to time.Time, stdType StdType) (io.ReadCloser, error) { options := types.ContainerLogsOptions{ ShowStdout: stdType&STDOUT != 0, ShowStderr: stdType&STDERR != 0, @@ -315,11 +307,11 @@ func (d *dockerClient) ContainerLogsBetweenDates(ctx context.Context, id string, return reader, nil } -func (d *dockerClient) Ping(ctx context.Context) (types.Ping, error) { +func (d *Client) Ping(ctx context.Context) (types.Ping, error) { return d.cli.Ping(ctx) } -func (d *dockerClient) Host() *Host { +func (d *Client) Host() *Host { return d.host } diff --git a/docker/client_test.go b/docker/client_test.go index 5e22f1ff..291abfad 100644 --- a/docker/client_test.go +++ b/docker/client_test.go @@ -19,7 +19,7 @@ import ( type mockedProxy struct { mock.Mock - dockerProxy + DockerCLI } func (m *mockedProxy) ContainerList(context.Context, types.ContainerListOptions) ([]types.Container, error) { @@ -53,7 +53,7 @@ func (m *mockedProxy) ContainerStats(ctx context.Context, containerID string, st func Test_dockerClient_ListContainers_null(t *testing.T) { proxy := new(mockedProxy) proxy.On("ContainerList", mock.Anything, mock.Anything).Return(nil, nil) - client := &dockerClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}} + client := &Client{proxy, filters.NewArgs(), &Host{ID: "localhost"}} list, err := client.ListContainers() assert.Empty(t, list, "list should be empty") @@ -65,7 +65,7 @@ func Test_dockerClient_ListContainers_null(t *testing.T) { func Test_dockerClient_ListContainers_error(t *testing.T) { proxy := new(mockedProxy) proxy.On("ContainerList", mock.Anything, mock.Anything).Return(nil, errors.New("test")) - client := &dockerClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}} + client := &Client{proxy, filters.NewArgs(), &Host{ID: "localhost"}} list, err := client.ListContainers() assert.Nil(t, list, "list should be nil") @@ -88,7 +88,7 @@ func Test_dockerClient_ListContainers_happy(t *testing.T) { proxy := new(mockedProxy) proxy.On("ContainerList", mock.Anything, mock.Anything).Return(containers, nil) - client := &dockerClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}} + client := &Client{proxy, filters.NewArgs(), &Host{ID: "localhost"}} list, err := client.ListContainers() require.NoError(t, err, "error should not return an error.") @@ -125,7 +125,7 @@ func Test_dockerClient_ContainerLogs_happy(t *testing.T) { options := types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: "300", Timestamps: true, Since: "since"} proxy.On("ContainerLogs", mock.Anything, id, options).Return(reader, nil) - client := &dockerClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}} + client := &Client{proxy, filters.NewArgs(), &Host{ID: "localhost"}} logReader, _ := client.ContainerLogs(context.Background(), id, "since", STDALL) actual, _ := io.ReadAll(logReader) @@ -139,7 +139,7 @@ func Test_dockerClient_ContainerLogs_error(t *testing.T) { proxy.On("ContainerLogs", mock.Anything, id, mock.Anything).Return(nil, errors.New("test")) - client := &dockerClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}} + client := &Client{proxy, filters.NewArgs(), &Host{ID: "localhost"}} reader, err := client.ContainerLogs(context.Background(), id, "", STDALL) @@ -166,7 +166,7 @@ func Test_dockerClient_FindContainer_happy(t *testing.T) { json := types.ContainerJSON{Config: &container.Config{Tty: false}} proxy.On("ContainerInspect", mock.Anything, "abcdefghijkl").Return(json, nil) - client := &dockerClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}} + client := &Client{proxy, filters.NewArgs(), &Host{ID: "localhost"}} container, err := client.FindContainer("abcdefghijkl") require.NoError(t, err, "error should not be thrown") @@ -195,7 +195,7 @@ func Test_dockerClient_FindContainer_error(t *testing.T) { proxy := new(mockedProxy) proxy.On("ContainerList", mock.Anything, mock.Anything).Return(containers, nil) - client := &dockerClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}} + client := &Client{proxy, filters.NewArgs(), &Host{ID: "localhost"}} _, err := client.FindContainer("not_valid") require.Error(t, err, "error should be thrown") diff --git a/docker/event_generator.go b/docker/event_generator.go index a04dfaf5..ee4435ae 100644 --- a/docker/event_generator.go +++ b/docker/event_generator.go @@ -8,7 +8,6 @@ import ( "fmt" "hash/fnv" "io" - "regexp" "strings" "sync" "time" @@ -197,38 +196,3 @@ func checkPosition(currentEvent *LogEvent, nextEvent *LogEvent) { currentEvent.Position = END } } - -var KEY_VALUE_REGEX = regexp.MustCompile(`level=(\w+)`) -var ANSI_COLOR_REGEX = regexp.MustCompile(`\x1b\[[0-9;]*m`) - -func guessLogLevel(logEvent *LogEvent) string { - switch value := logEvent.Message.(type) { - case string: - levels := []string{"error", "warn", "warning", "info", "debug", "trace", "fatal"} - stripped := ANSI_COLOR_REGEX.ReplaceAllString(value, "") // remove ansi color codes - for _, level := range levels { - if match, _ := regexp.MatchString("(?i)^"+level+"[^a-z]", stripped); match { - return level - } - - if strings.Contains(value, "["+strings.ToUpper(level)+"]") { - return level - } - - if strings.Contains(value, " "+strings.ToUpper(level)+" ") { - return level - } - } - - if matches := KEY_VALUE_REGEX.FindStringSubmatch(value); matches != nil { - return matches[1] - } - - case map[string]interface{}: - if level, ok := value["level"].(string); ok { - return level - } - } - - return "" -} diff --git a/docker/level_guesser.go b/docker/level_guesser.go new file mode 100644 index 00000000..d4f8ecac --- /dev/null +++ b/docker/level_guesser.go @@ -0,0 +1,41 @@ +package docker + +import ( + "regexp" + "strings" +) + +var KEY_VALUE_REGEX = regexp.MustCompile(`level=(\w+)`) +var ANSI_COLOR_REGEX = regexp.MustCompile(`\x1b\[[0-9;]*m`) + +func guessLogLevel(logEvent *LogEvent) string { + switch value := logEvent.Message.(type) { + case string: + levels := []string{"error", "warn", "warning", "info", "debug", "trace", "fatal"} + stripped := ANSI_COLOR_REGEX.ReplaceAllString(value, "") // remove ansi color codes + for _, level := range levels { + if match, _ := regexp.MatchString("(?i)^"+level+"[^a-z]", stripped); match { + return level + } + + if strings.Contains(value, "["+strings.ToUpper(level)+"]") { + return level + } + + if strings.Contains(value, " "+strings.ToUpper(level)+" ") { + return level + } + } + + if matches := KEY_VALUE_REGEX.FindStringSubmatch(value); matches != nil { + return matches[1] + } + + case map[string]interface{}: + if level, ok := value["level"].(string); ok { + return level + } + } + + return "" +} diff --git a/main.go b/main.go index cf6b4b9d..64feeb43 100644 --- a/main.go +++ b/main.go @@ -137,8 +137,10 @@ func doStartEvent(arg args) { } } -func createClients(args args, localClientFactory func(map[string][]string) (docker.Client, error), remoteClientFactory func(map[string][]string, docker.Host) (docker.Client, error)) map[string]docker.Client { - clients := make(map[string]docker.Client) +func createClients(args args, + localClientFactory func(map[string][]string) (*docker.Client, error), + remoteClientFactory func(map[string][]string, docker.Host) (*docker.Client, error)) map[string]web.DockerClient { + clients := make(map[string]web.DockerClient) if localClient := createLocalClient(args, localClientFactory); localClient != nil { clients[localClient.Host().ID] = localClient @@ -166,7 +168,7 @@ func createClients(args args, localClientFactory func(map[string][]string) (dock return clients } -func createServer(args args, clients map[string]docker.Client) *http.Server { +func createServer(args args, clients map[string]web.DockerClient) *http.Server { _, dev := os.LookupEnv("DEV") config := web.Config{ Addr: args.Addr, @@ -206,7 +208,7 @@ func createServer(args args, clients map[string]docker.Client) *http.Server { return web.CreateServer(clients, assets, config) } -func createLocalClient(args args, localClientFactory func(map[string][]string) (docker.Client, error)) docker.Client { +func createLocalClient(args args, localClientFactory func(map[string][]string) (*docker.Client, error)) *docker.Client { for i := 1; ; i++ { dockerClient, err := localClientFactory(args.Filter) if err == nil { diff --git a/main_test.go b/main_test.go index c1b7dfd9..cebc3a20 100644 --- a/main_test.go +++ b/main_test.go @@ -1,37 +1,34 @@ package main import ( + "context" "errors" "testing" "github.com/amir20/dozzle/docker" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) -type fakeClient struct { - docker.Client +type fakeCLI struct { + docker.DockerCLI mock.Mock } -func (f *fakeClient) ListContainers() ([]docker.Container, error) { +func (f *fakeCLI) ContainerList(context.Context, types.ContainerListOptions) ([]types.Container, error) { args := f.Called() - return args.Get(0).([]docker.Container), args.Error(1) -} - -func (f *fakeClient) Host() *docker.Host { - args := f.Called() - return args.Get(0).(*docker.Host) + return args.Get(0).([]types.Container), args.Error(1) } func Test_valid_localhost(t *testing.T) { - fakeClientFactory := func(filter map[string][]string) (docker.Client, error) { - client := new(fakeClient) - client.On("ListContainers").Return([]docker.Container{}, nil) - client.On("Host").Return(&docker.Host{ + client := new(fakeCLI) + client.On("ContainerList").Return([]types.Container{}, nil) + fakeClientFactory := func(filter map[string][]string) (*docker.Client, error) { + return docker.NewClient(client, filters.NewArgs(), &docker.Host{ ID: "localhost", - }) - return client, nil + }), nil } args := args{} @@ -39,16 +36,16 @@ func Test_valid_localhost(t *testing.T) { actualClient := createLocalClient(args, fakeClientFactory) assert.NotNil(t, actualClient) + client.AssertExpectations(t) } func Test_invalid_localhost(t *testing.T) { - fakeClientFactory := func(filter map[string][]string) (docker.Client, error) { - client := new(fakeClient) - client.On("ListContainers").Return([]docker.Container{}, errors.New("error")) - client.On("Host").Return(&docker.Host{ + client := new(fakeCLI) + client.On("ContainerList").Return([]types.Container{}, errors.New("error")) + fakeClientFactory := func(filter map[string][]string) (*docker.Client, error) { + return docker.NewClient(client, filters.NewArgs(), &docker.Host{ ID: "localhost", - }) - return client, nil + }), nil } args := args{} @@ -56,26 +53,24 @@ func Test_invalid_localhost(t *testing.T) { actualClient := createLocalClient(args, fakeClientFactory) assert.Nil(t, actualClient) + client.AssertExpectations(t) } func Test_valid_remote(t *testing.T) { - fakeLocalClientFactory := func(filter map[string][]string) (docker.Client, error) { - client := new(fakeClient) - client.On("ListContainers").Return([]docker.Container{}, errors.New("error")) - client.On("Host").Return(&docker.Host{ + local := new(fakeCLI) + local.On("ContainerList").Return([]types.Container{}, errors.New("error")) + fakeLocalClientFactory := func(filter map[string][]string) (*docker.Client, error) { + return docker.NewClient(local, filters.NewArgs(), &docker.Host{ ID: "localhost", - }) - - return client, nil + }), nil } - fakeRemoteClientFactory := func(filter map[string][]string, host docker.Host) (docker.Client, error) { - client := new(fakeClient) - client.On("ListContainers").Return([]docker.Container{}, nil) - client.On("Host").Return(&docker.Host{ + remote := new(fakeCLI) + remote.On("ContainerList").Return([]types.Container{}, nil) + fakeRemoteClientFactory := func(filter map[string][]string, host docker.Host) (*docker.Client, error) { + return docker.NewClient(remote, filters.NewArgs(), &docker.Host{ ID: "test", - }) - return client, nil + }), nil } args := args{ @@ -87,27 +82,26 @@ func Test_valid_remote(t *testing.T) { assert.Equal(t, 1, len(clients)) assert.Contains(t, clients, "test") assert.NotContains(t, clients, "localhost") + local.AssertExpectations(t) + remote.AssertExpectations(t) } func Test_valid_remote_and_local(t *testing.T) { - fakeLocalClientFactory := func(filter map[string][]string) (docker.Client, error) { - client := new(fakeClient) - client.On("ListContainers").Return([]docker.Container{}, nil) - client.On("Host").Return(&docker.Host{ + local := new(fakeCLI) + local.On("ContainerList").Return([]types.Container{}, nil) + fakeLocalClientFactory := func(filter map[string][]string) (*docker.Client, error) { + return docker.NewClient(local, filters.NewArgs(), &docker.Host{ ID: "localhost", - }) - return client, nil + }), nil } - fakeRemoteClientFactory := func(filter map[string][]string, host docker.Host) (docker.Client, error) { - client := new(fakeClient) - client.On("ListContainers").Return([]docker.Container{}, nil) - client.On("Host").Return(&docker.Host{ + remote := new(fakeCLI) + remote.On("ContainerList").Return([]types.Container{}, nil) + fakeRemoteClientFactory := func(filter map[string][]string, host docker.Host) (*docker.Client, error) { + return docker.NewClient(remote, filters.NewArgs(), &docker.Host{ ID: "test", - }) - return client, nil + }), nil } - args := args{ RemoteHost: []string{"tcp://test:2375"}, } @@ -117,24 +111,24 @@ func Test_valid_remote_and_local(t *testing.T) { assert.Equal(t, 2, len(clients)) assert.Contains(t, clients, "test") assert.Contains(t, clients, "localhost") + local.AssertExpectations(t) + remote.AssertExpectations(t) } func Test_no_clients(t *testing.T) { - fakeLocalClientFactory := func(filter map[string][]string) (docker.Client, error) { - client := new(fakeClient) - client.On("ListContainers").Return([]docker.Container{}, errors.New("error")) - client.On("Host").Return(&docker.Host{ - ID: "localhost", - }) - return client, nil - } + local := new(fakeCLI) + local.On("ContainerList").Return([]types.Container{}, errors.New("error")) + fakeLocalClientFactory := func(filter map[string][]string) (*docker.Client, error) { - fakeRemoteClientFactory := func(filter map[string][]string, host docker.Host) (docker.Client, error) { - client := new(fakeClient) - client.On("Host").Return(&docker.Host{ + return docker.NewClient(local, filters.NewArgs(), &docker.Host{ + ID: "localhost", + }), nil + } + fakeRemoteClientFactory := func(filter map[string][]string, host docker.Host) (*docker.Client, error) { + client := new(fakeCLI) + return docker.NewClient(client, filters.NewArgs(), &docker.Host{ ID: "test", - }) - return client, nil + }), nil } args := args{} @@ -142,4 +136,5 @@ func Test_no_clients(t *testing.T) { clients := createClients(args, fakeLocalClientFactory, fakeRemoteClientFactory) assert.Equal(t, 0, len(clients)) + local.AssertExpectations(t) } diff --git a/web/events.go b/web/events.go index ebf147fe..8325723f 100644 --- a/web/events.go +++ b/web/events.go @@ -39,11 +39,11 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) { for _, client := range h.clients { client.Events(ctx, events) - go func(client docker.Client) { + go func(client DockerClient) { defer wg.Done() if containers, err := client.ListContainers(); err == nil { results <- containers - go func(client docker.Client) { + go func(client DockerClient) { for _, c := range containers { if c.State == "running" { if err := client.ContainerStats(ctx, c.ID, stats); err != nil && !errors.Is(err, context.Canceled) { diff --git a/web/routes.go b/web/routes.go index 5b3f8cfd..265da3b6 100644 --- a/web/routes.go +++ b/web/routes.go @@ -1,12 +1,14 @@ package web import ( + "context" "encoding/json" "fmt" "html/template" "io" "io/fs" "sort" + "time" "net/http" "os" @@ -15,6 +17,7 @@ import ( "github.com/amir20/dozzle/analytics" "github.com/amir20/dozzle/docker" + "github.com/docker/docker/api/types" "github.com/go-chi/chi/v5" log "github.com/sirupsen/logrus" @@ -33,13 +36,24 @@ type Config struct { } type handler struct { - clients map[string]docker.Client + clients map[string]DockerClient content fs.FS config *Config } -// CreateServer creates a service for http handler -func CreateServer(clients map[string]docker.Client, content fs.FS, config Config) *http.Server { +// Client is a proxy around the docker client +type DockerClient interface { + ListContainers() ([]docker.Container, error) + FindContainer(string) (docker.Container, error) + ContainerLogs(context.Context, string, string, docker.StdType) (io.ReadCloser, error) + Events(context.Context, chan<- docker.ContainerEvent) <-chan error + ContainerLogsBetweenDates(context.Context, string, time.Time, time.Time, docker.StdType) (io.ReadCloser, error) + ContainerStats(context.Context, string, chan<- docker.ContainerStat) error + Ping(context.Context) (types.Ping, error) + Host() *docker.Host +} + +func CreateServer(clients map[string]DockerClient, content fs.FS, config Config) *http.Server { handler := &handler{ clients: clients, content: content, @@ -98,7 +112,7 @@ func (h *handler) index(w http.ResponseWriter, req *http.Request) { go func() { host, _ := os.Hostname() - var client docker.Client + var client DockerClient for _, v := range h.clients { client = v break @@ -216,7 +230,7 @@ func (h *handler) version(w http.ResponseWriter, r *http.Request) { func (h *handler) healthcheck(w http.ResponseWriter, r *http.Request) { log.Trace("Executing healthcheck request") - var client docker.Client + var client DockerClient for _, v := range h.clients { client = v break @@ -230,7 +244,7 @@ func (h *handler) healthcheck(w http.ResponseWriter, r *http.Request) { } } -func (h *handler) clientFromRequest(r *http.Request) docker.Client { +func (h *handler) clientFromRequest(r *http.Request) DockerClient { host := chi.URLParam(r, "host") if host == "" { diff --git a/web/routes_test.go b/web/routes_test.go index 848dea6d..6ab3b89e 100644 --- a/web/routes_test.go +++ b/web/routes_test.go @@ -17,7 +17,7 @@ import ( type MockedClient struct { mock.Mock - docker.Client + DockerClient } func (m *MockedClient) FindContainer(id string) (docker.Container, error) { @@ -54,7 +54,7 @@ func (m *MockedClient) Host() *docker.Host { return args.Get(0).(*docker.Host) } -func createHandler(client docker.Client, content fs.FS, config Config) *chi.Mux { +func createHandler(client DockerClient, content fs.FS, config Config) *chi.Mux { if client == nil { client = new(MockedClient) client.(*MockedClient).On("ListContainers").Return([]docker.Container{}, nil) @@ -69,7 +69,7 @@ func createHandler(client docker.Client, content fs.FS, config Config) *chi.Mux content = afero.NewIOFS(fs) } - clients := map[string]docker.Client{ + clients := map[string]DockerClient{ "localhost": client, } return createRouter(&handler{ @@ -79,6 +79,6 @@ func createHandler(client docker.Client, content fs.FS, config Config) *chi.Mux }) } -func createDefaultHandler(client docker.Client) *chi.Mux { +func createDefaultHandler(client DockerClient) *chi.Mux { return createHandler(client, nil, Config{Base: "/"}) }