mirror of
https://github.com/amir20/dozzle.git
synced 2025-12-21 21:33:18 +01:00
feat: adds ability to support labels with | delimeter (#2276)
* feat: adds ability to support labels with | delimeter * fixes tests * updates docs
This commit is contained in:
2
.reflex
2
.reflex
@@ -1 +1 @@
|
||||
-r '\.(go)$' -R 'node_modules' -G '*_test.go' -s -- go run main.go --level debug --remote-host tcp://64.225.88.189:2376
|
||||
-r '\.(go)$' -R 'node_modules' -G '*_test.go' -s -- go run main.go --level debug --remote-host tcp://64.225.88.189:2376|clashleaders.com
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<nav class="breadcrumb menu-label" aria-label="breadcrumbs">
|
||||
<ul v-if="sessionHost">
|
||||
<li>
|
||||
<a href="#" @click.prevent="setHost(null)">{{ sessionHost }}</a>
|
||||
<a href="#" @click.prevent="setHost(null)">{{ hosts[sessionHost].name }}</a>
|
||||
</li>
|
||||
<li class="is-active">
|
||||
<a href="#" aria-current="page">{{ $t("label.containers") }}</a>
|
||||
@@ -16,7 +16,7 @@
|
||||
<transition :name="sessionHost ? 'slide-left' : 'slide-right'" mode="out-in">
|
||||
<ul class="menu-list" v-if="!sessionHost">
|
||||
<li v-for="host in config.hosts">
|
||||
<a @click.prevent="setHost(host)">{{ host }}</a>
|
||||
<a @click.prevent="setHost(host.host)">{{ host.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="menu-list" v-else>
|
||||
@@ -85,6 +85,13 @@ const sortedContainers = computed(() =>
|
||||
})
|
||||
);
|
||||
|
||||
const hosts = computed(() =>
|
||||
config.hosts.reduce((acc, item) => {
|
||||
acc[item.host] = item;
|
||||
return acc;
|
||||
}, {} as Record<string, { name: string; host: string }>)
|
||||
);
|
||||
|
||||
const activeContainersById = computed(() =>
|
||||
activeContainers.value.reduce((acc, item) => {
|
||||
acc[item.id] = item;
|
||||
@@ -101,7 +108,8 @@ const activeContainersById = computed(() =>
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
li.exited a, li.dead a {
|
||||
li.exited a,
|
||||
li.dead a {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,15 +26,15 @@
|
||||
<o-dropdown v-model="sessionHost" aria-role="list">
|
||||
<template #trigger>
|
||||
<o-button variant="primary" type="button" size="small">
|
||||
<span>{{ sessionHost }}</span>
|
||||
<span>{{ sessionHost ? hosts[sessionHost].name : "" }}</span>
|
||||
<span class="icon">
|
||||
<carbon:caret-down />
|
||||
</span>
|
||||
</o-button>
|
||||
</template>
|
||||
|
||||
<o-dropdown-item :value="value" aria-role="listitem" v-for="value in config.hosts" :key="value">
|
||||
<span>{{ value }}</span>
|
||||
<o-dropdown-item :value="value.host" aria-role="listitem" v-for="value in config.hosts" :key="value">
|
||||
<span>{{ value.name }}</span>
|
||||
</o-dropdown-item>
|
||||
</o-dropdown>
|
||||
</div>
|
||||
@@ -111,6 +111,13 @@ const sortedContainers = computed(() =>
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const hosts = computed(() =>
|
||||
config.hosts.reduce((acc, item) => {
|
||||
acc[item.host] = item;
|
||||
return acc;
|
||||
}, {} as Record<string, { name: string; host: string }>)
|
||||
);
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
aside {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Container } from "@/models/Container";
|
||||
const sessionHost = useSessionStorage<string | null>("host", null);
|
||||
|
||||
if (config.hosts.length === 1 && !sessionHost.value) {
|
||||
sessionHost.value = config.hosts[0];
|
||||
sessionHost.value = config.hosts[0].host;
|
||||
}
|
||||
|
||||
function persistentVisibleKeys(container: ComputedRef<Container>) {
|
||||
|
||||
@@ -7,7 +7,7 @@ interface Config {
|
||||
secured: boolean;
|
||||
maxLogs: number;
|
||||
hostname: string;
|
||||
hosts: string[];
|
||||
hosts: { name: string; host: string }[];
|
||||
}
|
||||
|
||||
const pageConfig = JSON.parse(text);
|
||||
|
||||
@@ -5,8 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
@@ -25,7 +23,7 @@ import (
|
||||
type dockerClient struct {
|
||||
cli dockerProxy
|
||||
filters filters.Args
|
||||
host string
|
||||
host *Host
|
||||
}
|
||||
|
||||
type StdType int
|
||||
@@ -69,7 +67,7 @@ type Client interface {
|
||||
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() string
|
||||
Host() *Host
|
||||
}
|
||||
|
||||
// NewClientWithFilters creates a new instance of Client with docker filters
|
||||
@@ -89,10 +87,10 @@ func NewClientWithFilters(f map[string][]string) (Client, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dockerClient{cli, filterArgs, "localhost"}, nil
|
||||
return &dockerClient{cli, filterArgs, &Host{Name: "localhost", Host: "localhost"}}, nil
|
||||
}
|
||||
|
||||
func NewClientWithTlsAndFilter(f map[string][]string, connection string) (Client, error) {
|
||||
func NewClientWithTlsAndFilter(f map[string][]string, host Host) (Client, error) {
|
||||
filterArgs := filters.NewArgs()
|
||||
for key, values := range f {
|
||||
for _, value := range values {
|
||||
@@ -102,38 +100,19 @@ func NewClientWithTlsAndFilter(f map[string][]string, connection string) (Client
|
||||
|
||||
log.Debugf("filterArgs = %v", filterArgs)
|
||||
|
||||
remoteUrl, err := url.Parse(connection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if remoteUrl.Scheme != "tcp" {
|
||||
if host.URL.Scheme != "tcp" {
|
||||
log.Fatal("Only tcp scheme is supported")
|
||||
}
|
||||
|
||||
host := remoteUrl.Hostname()
|
||||
basePath, err := filepath.Abs("./certs")
|
||||
if err != nil {
|
||||
log.Fatalf("error converting certs path to absolute: %s", err)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(filepath.Join(basePath, host)); !os.IsNotExist(err) {
|
||||
basePath = filepath.Join(basePath, host)
|
||||
}
|
||||
|
||||
cacertPath := filepath.Join(basePath, "ca.pem")
|
||||
certPath := filepath.Join(basePath, "cert.pem")
|
||||
keyPath := filepath.Join(basePath, "key.pem")
|
||||
|
||||
opts := []client.Opt{
|
||||
client.WithHost(connection),
|
||||
client.WithHost(host.URL.String()),
|
||||
}
|
||||
|
||||
if _, err := os.Stat(cacertPath); os.IsNotExist(err) {
|
||||
log.Debugf("%s does not exist, using plain HTTP", cacertPath)
|
||||
if host.ValidCerts {
|
||||
log.Debugf("Using TLS client config with certs at: %s", filepath.Dir(host.CertPath))
|
||||
opts = append(opts, client.WithTLSClientConfig(host.CACertPath, host.CertPath, host.KeyPath))
|
||||
} else {
|
||||
log.Debugf("Using TLS client config with certs at: %s", basePath)
|
||||
opts = append(opts, client.WithTLSClientConfig(cacertPath, certPath, keyPath))
|
||||
log.Debugf("No valid certs found, using plain TCP")
|
||||
}
|
||||
|
||||
opts = append(opts, client.WithAPIVersionNegotiation())
|
||||
@@ -144,7 +123,7 @@ func NewClientWithTlsAndFilter(f map[string][]string, connection string) (Client
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dockerClient{cli, filterArgs, host}, nil
|
||||
return &dockerClient{cli, filterArgs, &host}, nil
|
||||
}
|
||||
|
||||
func (d *dockerClient) FindContainer(id string) (Container, error) {
|
||||
@@ -191,7 +170,7 @@ func (d *dockerClient) ListContainers() ([]Container, error) {
|
||||
Created: c.Created,
|
||||
State: c.State,
|
||||
Status: c.Status,
|
||||
Host: d.host,
|
||||
Host: d.host.Host,
|
||||
Health: findBetweenParentheses(c.Status),
|
||||
}
|
||||
containers = append(containers, container)
|
||||
@@ -308,7 +287,7 @@ func (d *dockerClient) Events(ctx context.Context, messages chan<- ContainerEven
|
||||
messages <- ContainerEvent{
|
||||
ActorID: message.Actor.ID[:12],
|
||||
Name: message.Action,
|
||||
Host: d.host,
|
||||
Host: d.host.Host,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -371,7 +350,7 @@ func (d *dockerClient) Ping(ctx context.Context) (types.Ping, error) {
|
||||
return d.cli.Ping(ctx)
|
||||
}
|
||||
|
||||
func (d *dockerClient) Host() string {
|
||||
func (d *dockerClient) Host() *Host {
|
||||
return d.host
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,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(), "localhost"}
|
||||
client := &dockerClient{proxy, filters.NewArgs(), &Host{Host: "localhost"}}
|
||||
|
||||
list, err := client.ListContainers()
|
||||
assert.Empty(t, list, "list should be empty")
|
||||
@@ -66,7 +66,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(), "localhost"}
|
||||
client := &dockerClient{proxy, filters.NewArgs(), &Host{Host: "localhost"}}
|
||||
|
||||
list, err := client.ListContainers()
|
||||
assert.Nil(t, list, "list should be nil")
|
||||
@@ -89,7 +89,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(), "localhost"}
|
||||
client := &dockerClient{proxy, filters.NewArgs(), &Host{Host: "localhost"}}
|
||||
|
||||
list, err := client.ListContainers()
|
||||
require.NoError(t, err, "error should not return an error.")
|
||||
@@ -129,7 +129,7 @@ func Test_dockerClient_ContainerLogs_happy(t *testing.T) {
|
||||
json := types.ContainerJSON{Config: &container.Config{Tty: false}}
|
||||
proxy.On("ContainerInspect", mock.Anything, id).Return(json, nil)
|
||||
|
||||
client := &dockerClient{proxy, filters.NewArgs(), "localhost"}
|
||||
client := &dockerClient{proxy, filters.NewArgs(), &Host{Host: "localhost"}}
|
||||
logReader, _ := client.ContainerLogs(context.Background(), id, "since", STDALL)
|
||||
|
||||
actual, _ := io.ReadAll(logReader)
|
||||
@@ -150,7 +150,7 @@ func Test_dockerClient_ContainerLogs_happy_with_tty(t *testing.T) {
|
||||
json := types.ContainerJSON{Config: &container.Config{Tty: true}}
|
||||
proxy.On("ContainerInspect", mock.Anything, id).Return(json, nil)
|
||||
|
||||
client := &dockerClient{proxy, filters.NewArgs(), "localhost"}
|
||||
client := &dockerClient{proxy, filters.NewArgs(), &Host{Host: "localhost"}}
|
||||
logReader, _ := client.ContainerLogs(context.Background(), id, "", STDALL)
|
||||
|
||||
actual, _ := io.ReadAll(logReader)
|
||||
@@ -165,7 +165,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(), "localhost"}
|
||||
client := &dockerClient{proxy, filters.NewArgs(), &Host{Host: "localhost"}}
|
||||
|
||||
reader, err := client.ContainerLogs(context.Background(), id, "", STDALL)
|
||||
|
||||
@@ -188,7 +188,7 @@ func Test_dockerClient_FindContainer_happy(t *testing.T) {
|
||||
|
||||
proxy := new(mockedProxy)
|
||||
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(containers, nil)
|
||||
client := &dockerClient{proxy, filters.NewArgs(), "localhost"}
|
||||
client := &dockerClient{proxy, filters.NewArgs(), &Host{Host: "localhost"}}
|
||||
|
||||
container, err := client.FindContainer("abcdefghijkl")
|
||||
require.NoError(t, err, "error should not be thrown")
|
||||
@@ -216,7 +216,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(), "localhost"}
|
||||
client := &dockerClient{proxy, filters.NewArgs(), &Host{Host: "localhost"}}
|
||||
|
||||
_, err := client.FindContainer("not_valid")
|
||||
require.Error(t, err, "error should be thrown")
|
||||
|
||||
68
docker/host.go
Normal file
68
docker/host.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Host struct {
|
||||
Name string `json:"name"`
|
||||
Host string `json:"host"`
|
||||
URL *url.URL `json:"-"`
|
||||
CertPath string `json:"-"`
|
||||
CACertPath string `json:"-"`
|
||||
KeyPath string `json:"-"`
|
||||
ValidCerts bool `json:"-"`
|
||||
}
|
||||
|
||||
func ParseConnection(connection string) (Host, error) {
|
||||
parts := strings.Split(connection, "|")
|
||||
if len(parts) > 2 {
|
||||
return Host{}, fmt.Errorf("invalid connection string: %s", connection)
|
||||
}
|
||||
|
||||
remoteUrl, err := url.Parse(parts[0])
|
||||
if err != nil {
|
||||
return Host{}, err
|
||||
}
|
||||
|
||||
name := remoteUrl.Hostname()
|
||||
if len(parts) == 2 {
|
||||
name = parts[1]
|
||||
}
|
||||
|
||||
basePath, err := filepath.Abs("./certs")
|
||||
if err != nil {
|
||||
log.Fatalf("error converting certs path to absolute: %s", err)
|
||||
}
|
||||
|
||||
host := remoteUrl.Hostname()
|
||||
if _, err := os.Stat(filepath.Join(basePath, host)); !os.IsNotExist(err) {
|
||||
basePath = filepath.Join(basePath, host)
|
||||
}
|
||||
|
||||
cacertPath := filepath.Join(basePath, "ca.pem")
|
||||
certPath := filepath.Join(basePath, "cert.pem")
|
||||
keyPath := filepath.Join(basePath, "key.pem")
|
||||
|
||||
hasCerts := true
|
||||
if _, err := os.Stat(cacertPath); os.IsNotExist(err) {
|
||||
cacertPath = ""
|
||||
hasCerts = false
|
||||
}
|
||||
|
||||
return Host{
|
||||
Name: name,
|
||||
Host: host,
|
||||
URL: remoteUrl,
|
||||
CertPath: certPath,
|
||||
CACertPath: cacertPath,
|
||||
KeyPath: keyPath,
|
||||
ValidCerts: hasCerts,
|
||||
}, nil
|
||||
|
||||
}
|
||||
4
docs/components.d.ts
vendored
4
docs/components.d.ts
vendored
@@ -3,11 +3,9 @@
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
import '@vue/runtime-core'
|
||||
|
||||
export {}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
BrowserWindow: typeof import('./.vitepress/theme/components/BrowserWindow.vue')['default']
|
||||
HeroVideo: typeof import('./.vitepress/theme/components/HeroVideo.vue')['default']
|
||||
|
||||
@@ -53,3 +53,29 @@ docker run --volume=/var/run/docker.sock:/var/run/docker.sock -p 8080:8080 amir2
|
||||
::: warning
|
||||
Exposing `docker.sock` publicly is not safe. Only use a proxy for an internal network where all clients are trusted.
|
||||
:::
|
||||
|
||||
## Adding labels to hosts
|
||||
|
||||
`--remote-host` supports host labels by appending them to the connection string with `|`. For example, `--remote-host tcp://123.1.1.1:2375|foobar.com` will use foobar.com as the label in the UI. A full example of this using the CLI or compose are:
|
||||
|
||||
::: code-group
|
||||
|
||||
```sh
|
||||
docker run --volume=/var/run/docker.sock:/var/run/docker.sock -p 8080:8080 amir20/dozzle --remote-host tcp://123.1.1.1:2375|foobar.com
|
||||
```
|
||||
|
||||
```yaml [docker-compose.yml]
|
||||
version: "3"
|
||||
services:
|
||||
dozzle:
|
||||
image: amir20/dozzle:latest
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /path/to/certs:/certs
|
||||
ports:
|
||||
- 8080:8080
|
||||
environment:
|
||||
DOZZLE_REMOTE_HOST: tcp://167.99.1.1:2376|foo.com,tcp://167.99.1.2:2376|bar.com
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
19
main.go
19
main.go
@@ -137,24 +137,29 @@ func doStartEvent(arg args) {
|
||||
}
|
||||
}
|
||||
|
||||
func createClients(args args, localClientFactory func(map[string][]string) (docker.Client, error), remoteClientFactory func(map[string][]string, string) (docker.Client, error)) 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]docker.Client {
|
||||
clients := make(map[string]docker.Client)
|
||||
|
||||
if localClient := createLocalClient(args, localClientFactory); localClient != nil {
|
||||
clients[localClient.Host()] = localClient
|
||||
clients[localClient.Host().Host] = localClient
|
||||
}
|
||||
|
||||
for _, host := range args.RemoteHost {
|
||||
log.Infof("Creating client for %s", host)
|
||||
for _, remoteHost := range args.RemoteHost {
|
||||
host, err := docker.ParseConnection(remoteHost)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not parse remote host %s: %s", remoteHost, err)
|
||||
}
|
||||
log.Debugf("Creating remote client for %s with %+v", host.Name, host)
|
||||
log.Infof("Creating client for %s with %s", host.Name, host.URL.String())
|
||||
if client, err := remoteClientFactory(args.Filter, host); err == nil {
|
||||
if _, err := client.ListContainers(); err == nil {
|
||||
log.Debugf("Connected to local Docker Engine")
|
||||
clients[client.Host()] = client
|
||||
clients[client.Host().Host] = client
|
||||
} else {
|
||||
log.Warnf("Could not connect to remote host %s: %s", host, err)
|
||||
log.Warnf("Could not connect to remote host %s: %s", host.Host, err)
|
||||
}
|
||||
} else {
|
||||
log.Warnf("Could not create client for %s: %s", host, err)
|
||||
log.Warnf("Could not create client for %s: %s", host.Host, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
42
main_test.go
42
main_test.go
@@ -19,16 +19,18 @@ func (f *fakeClient) ListContainers() ([]docker.Container, error) {
|
||||
return args.Get(0).([]docker.Container), args.Error(1)
|
||||
}
|
||||
|
||||
func (f *fakeClient) Host() string {
|
||||
func (f *fakeClient) Host() *docker.Host {
|
||||
args := f.Called()
|
||||
return args.String(0)
|
||||
return args.Get(0).(*docker.Host)
|
||||
}
|
||||
|
||||
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("localhost")
|
||||
client.On("Host").Return(&docker.Host{
|
||||
Host: "localhost",
|
||||
})
|
||||
return client, nil
|
||||
}
|
||||
|
||||
@@ -43,7 +45,9 @@ 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("localhost")
|
||||
client.On("Host").Return(&docker.Host{
|
||||
Host: "localhost",
|
||||
})
|
||||
return client, nil
|
||||
}
|
||||
|
||||
@@ -58,15 +62,19 @@ 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("localhost")
|
||||
client.On("Host").Return(&docker.Host{
|
||||
Host: "localhost",
|
||||
})
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
fakeRemoteClientFactory := func(filter map[string][]string, host string) (docker.Client, error) {
|
||||
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("test")
|
||||
client.On("Host").Return(&docker.Host{
|
||||
Host: "test",
|
||||
})
|
||||
return client, nil
|
||||
}
|
||||
|
||||
@@ -85,14 +93,18 @@ 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("localhost")
|
||||
client.On("Host").Return(&docker.Host{
|
||||
Host: "localhost",
|
||||
})
|
||||
return client, nil
|
||||
}
|
||||
|
||||
fakeRemoteClientFactory := func(filter map[string][]string, host string) (docker.Client, error) {
|
||||
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("test")
|
||||
client.On("Host").Return(&docker.Host{
|
||||
Host: "test",
|
||||
})
|
||||
return client, nil
|
||||
}
|
||||
|
||||
@@ -111,13 +123,17 @@ 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("localhost")
|
||||
client.On("Host").Return(&docker.Host{
|
||||
Host: "localhost",
|
||||
})
|
||||
return client, nil
|
||||
}
|
||||
|
||||
fakeRemoteClientFactory := func(filter map[string][]string, host string) (docker.Client, error) {
|
||||
fakeRemoteClientFactory := func(filter map[string][]string, host docker.Host) (docker.Client, error) {
|
||||
client := new(fakeClient)
|
||||
client.On("Host").Return("test")
|
||||
client.On("Host").Return(&docker.Host{
|
||||
Host: "test",
|
||||
})
|
||||
return client, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"html/template"
|
||||
"io"
|
||||
"io/fs"
|
||||
"sort"
|
||||
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -154,26 +155,21 @@ func (h *handler) executeTemplate(w http.ResponseWriter, req *http.Request) {
|
||||
path = h.config.Base
|
||||
}
|
||||
|
||||
// Get all keys from hosts map
|
||||
hosts := make([]string, 0, len(h.clients))
|
||||
for k := range h.clients {
|
||||
hosts = append(hosts, k)
|
||||
hosts := make([]*docker.Host, 0, len(h.clients))
|
||||
for _, v := range h.clients {
|
||||
hosts = append(hosts, v.Host())
|
||||
}
|
||||
sort.Slice(hosts, func(i, j int) bool {
|
||||
return hosts[i].Name < hosts[j].Name
|
||||
})
|
||||
|
||||
config := struct {
|
||||
Base string `json:"base"`
|
||||
Version string `json:"version"`
|
||||
AuthorizationNeeded bool `json:"authorizationNeeded"`
|
||||
Secured bool `json:"secured"`
|
||||
Hostname string `json:"hostname"`
|
||||
Hosts []string `json:"hosts"`
|
||||
}{
|
||||
path,
|
||||
h.config.Version,
|
||||
h.isAuthorizationNeeded(req),
|
||||
secured,
|
||||
h.config.Hostname,
|
||||
hosts,
|
||||
config := map[string]interface{}{
|
||||
"base": path,
|
||||
"version": h.config.Version,
|
||||
"authorizationNeeded": h.isAuthorizationNeeded(req),
|
||||
"secured": secured,
|
||||
"hostname": h.config.Hostname,
|
||||
"hosts": hosts,
|
||||
}
|
||||
|
||||
data := map[string]interface{}{
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
func Test_createRoutes_index(t *testing.T) {
|
||||
fs := afero.NewMemMapFs()
|
||||
require.NoError(t, afero.WriteFile(fs, "index.html", []byte("index page"), 0644), "WriteFile should have no error.")
|
||||
|
||||
handler := createHandler(nil, afero.NewIOFS(fs), Config{Base: "/"})
|
||||
req, err := http.NewRequest("GET", "/", nil)
|
||||
require.NoError(t, err, "NewRequest should not return an error.")
|
||||
@@ -64,6 +65,7 @@ func Test_createRoutes_redirect_with_auth(t *testing.T) {
|
||||
func Test_createRoutes_foobar(t *testing.T) {
|
||||
fs := afero.NewMemMapFs()
|
||||
require.NoError(t, afero.WriteFile(fs, "index.html", []byte("foo page"), 0644), "WriteFile should have no error.")
|
||||
|
||||
handler := createHandler(nil, afero.NewIOFS(fs), Config{Base: "/foobar"})
|
||||
req, err := http.NewRequest("GET", "/foobar/", nil)
|
||||
require.NoError(t, err, "NewRequest should not return an error.")
|
||||
|
||||
@@ -49,15 +49,18 @@ func (m *MockedClient) ContainerLogsBetweenDates(ctx context.Context, id string,
|
||||
return args.Get(0).(io.ReadCloser), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedClient) Host() string {
|
||||
func (m *MockedClient) Host() *docker.Host {
|
||||
args := m.Called()
|
||||
return args.String(0)
|
||||
return args.Get(0).(*docker.Host)
|
||||
}
|
||||
|
||||
func createHandler(client docker.Client, content fs.FS, config Config) *chi.Mux {
|
||||
if client == nil {
|
||||
client = new(MockedClient)
|
||||
client.(*MockedClient).On("ListContainers").Return([]docker.Container{}, nil)
|
||||
client.(*MockedClient).On("Host").Return(&docker.Host{
|
||||
Host: "localhost",
|
||||
})
|
||||
}
|
||||
|
||||
if content == nil {
|
||||
|
||||
Reference in New Issue
Block a user