mirror of
https://github.com/amir20/dozzle.git
synced 2025-12-21 13:23:07 +01:00
chore: refactors to be more generic (#3594)
This commit is contained in:
@@ -13,7 +13,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/agent/pb"
|
"github.com/amir20/dozzle/internal/agent/pb"
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/amir20/dozzle/internal/utils"
|
"github.com/amir20/dozzle/internal/utils"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
orderedmap "github.com/wk8/go-ordered-map/v2"
|
orderedmap "github.com/wk8/go-ordered-map/v2"
|
||||||
@@ -89,7 +89,7 @@ func rpcErrToErr(err error) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) LogsBetweenDates(ctx context.Context, containerID string, since time.Time, until time.Time, std docker.StdType) (<-chan *docker.LogEvent, error) {
|
func (c *Client) LogsBetweenDates(ctx context.Context, containerID string, since time.Time, until time.Time, std container.StdType) (<-chan *container.LogEvent, error) {
|
||||||
stream, err := c.client.LogsBetweenDates(ctx, &pb.LogsBetweenDatesRequest{
|
stream, err := c.client.LogsBetweenDates(ctx, &pb.LogsBetweenDatesRequest{
|
||||||
ContainerId: containerID,
|
ContainerId: containerID,
|
||||||
Since: timestamppb.New(since),
|
Since: timestamppb.New(since),
|
||||||
@@ -101,7 +101,7 @@ func (c *Client) LogsBetweenDates(ctx context.Context, containerID string, since
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
events := make(chan *docker.LogEvent)
|
events := make(chan *container.LogEvent)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
sendLogs(stream, events)
|
sendLogs(stream, events)
|
||||||
@@ -111,7 +111,7 @@ func (c *Client) LogsBetweenDates(ctx context.Context, containerID string, since
|
|||||||
return events, nil
|
return events, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) StreamContainerLogs(ctx context.Context, containerID string, since time.Time, std docker.StdType, events chan<- *docker.LogEvent) error {
|
func (c *Client) StreamContainerLogs(ctx context.Context, containerID string, since time.Time, std container.StdType, events chan<- *container.LogEvent) error {
|
||||||
stream, err := c.client.StreamLogs(ctx, &pb.StreamLogsRequest{
|
stream, err := c.client.StreamLogs(ctx, &pb.StreamLogsRequest{
|
||||||
ContainerId: containerID,
|
ContainerId: containerID,
|
||||||
Since: timestamppb.New(since),
|
Since: timestamppb.New(since),
|
||||||
@@ -125,7 +125,7 @@ func (c *Client) StreamContainerLogs(ctx context.Context, containerID string, si
|
|||||||
return sendLogs(stream, events)
|
return sendLogs(stream, events)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendLogs(stream pb.AgentService_StreamLogsClient, events chan<- *docker.LogEvent) error {
|
func sendLogs(stream pb.AgentService_StreamLogsClient, events chan<- *container.LogEvent) error {
|
||||||
for {
|
for {
|
||||||
resp, err := stream.Recv()
|
resp, err := stream.Recv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -151,19 +151,19 @@ func sendLogs(stream pb.AgentService_StreamLogsClient, events chan<- *docker.Log
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
events <- &docker.LogEvent{
|
events <- &container.LogEvent{
|
||||||
Id: resp.Event.Id,
|
Id: resp.Event.Id,
|
||||||
ContainerID: resp.Event.ContainerId,
|
ContainerID: resp.Event.ContainerId,
|
||||||
Message: message,
|
Message: message,
|
||||||
Timestamp: resp.Event.Timestamp.AsTime().Unix(),
|
Timestamp: resp.Event.Timestamp.AsTime().Unix(),
|
||||||
Position: docker.LogPosition(resp.Event.Position),
|
Position: container.LogPosition(resp.Event.Position),
|
||||||
Level: resp.Event.Level,
|
Level: resp.Event.Level,
|
||||||
Stream: resp.Event.Stream,
|
Stream: resp.Event.Stream,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) StreamRawBytes(ctx context.Context, containerID string, since time.Time, until time.Time, std docker.StdType) (io.ReadCloser, error) {
|
func (c *Client) StreamRawBytes(ctx context.Context, containerID string, since time.Time, until time.Time, std container.StdType) (io.ReadCloser, error) {
|
||||||
out, err := c.client.StreamRawBytes(context.Background(), &pb.StreamRawBytesRequest{
|
out, err := c.client.StreamRawBytes(context.Background(), &pb.StreamRawBytesRequest{
|
||||||
ContainerId: containerID,
|
ContainerId: containerID,
|
||||||
Since: timestamppb.New(since),
|
Since: timestamppb.New(since),
|
||||||
@@ -199,7 +199,7 @@ func (c *Client) StreamRawBytes(ctx context.Context, containerID string, since t
|
|||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) StreamStats(ctx context.Context, stats chan<- docker.ContainerStat) error {
|
func (c *Client) StreamStats(ctx context.Context, stats chan<- container.ContainerStat) error {
|
||||||
stream, err := c.client.StreamStats(ctx, &pb.StreamStatsRequest{})
|
stream, err := c.client.StreamStats(ctx, &pb.StreamStatsRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -211,7 +211,7 @@ func (c *Client) StreamStats(ctx context.Context, stats chan<- docker.ContainerS
|
|||||||
return rpcErrToErr(err)
|
return rpcErrToErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
stats <- docker.ContainerStat{
|
stats <- container.ContainerStat{
|
||||||
CPUPercent: resp.Stat.CpuPercent,
|
CPUPercent: resp.Stat.CpuPercent,
|
||||||
MemoryPercent: resp.Stat.MemoryPercent,
|
MemoryPercent: resp.Stat.MemoryPercent,
|
||||||
MemoryUsage: resp.Stat.MemoryUsage,
|
MemoryUsage: resp.Stat.MemoryUsage,
|
||||||
@@ -220,7 +220,7 @@ func (c *Client) StreamStats(ctx context.Context, stats chan<- docker.ContainerS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) StreamEvents(ctx context.Context, events chan<- docker.ContainerEvent) error {
|
func (c *Client) StreamEvents(ctx context.Context, events chan<- container.ContainerEvent) error {
|
||||||
stream, err := c.client.StreamEvents(ctx, &pb.StreamEventsRequest{})
|
stream, err := c.client.StreamEvents(ctx, &pb.StreamEventsRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -232,7 +232,7 @@ func (c *Client) StreamEvents(ctx context.Context, events chan<- docker.Containe
|
|||||||
return rpcErrToErr(err)
|
return rpcErrToErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
events <- docker.ContainerEvent{
|
events <- container.ContainerEvent{
|
||||||
ActorID: resp.Event.ActorId,
|
ActorID: resp.Event.ActorId,
|
||||||
Name: resp.Event.Name,
|
Name: resp.Event.Name,
|
||||||
Host: resp.Event.Host,
|
Host: resp.Event.Host,
|
||||||
@@ -241,7 +241,7 @@ func (c *Client) StreamEvents(ctx context.Context, events chan<- docker.Containe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) StreamNewContainers(ctx context.Context, containers chan<- docker.Container) error {
|
func (c *Client) StreamNewContainers(ctx context.Context, containers chan<- container.Container) error {
|
||||||
stream, err := c.client.StreamContainerStarted(ctx, &pb.StreamContainerStartedRequest{})
|
stream, err := c.client.StreamContainerStarted(ctx, &pb.StreamContainerStartedRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -253,7 +253,7 @@ func (c *Client) StreamNewContainers(ctx context.Context, containers chan<- dock
|
|||||||
return rpcErrToErr(err)
|
return rpcErrToErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
containers <- docker.Container{
|
containers <- container.Container{
|
||||||
ID: resp.Container.Id,
|
ID: resp.Container.Id,
|
||||||
Name: resp.Container.Name,
|
Name: resp.Container.Name,
|
||||||
Image: resp.Container.Image,
|
Image: resp.Container.Image,
|
||||||
@@ -271,16 +271,16 @@ func (c *Client) StreamNewContainers(ctx context.Context, containers chan<- dock
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) FindContainer(ctx context.Context, containerID string) (docker.Container, error) {
|
func (c *Client) FindContainer(ctx context.Context, containerID string) (container.Container, error) {
|
||||||
response, err := c.client.FindContainer(ctx, &pb.FindContainerRequest{ContainerId: containerID})
|
response, err := c.client.FindContainer(ctx, &pb.FindContainerRequest{ContainerId: containerID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return docker.Container{}, err
|
return container.Container{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var stats []docker.ContainerStat
|
var stats []container.ContainerStat
|
||||||
|
|
||||||
for _, stat := range response.Container.Stats {
|
for _, stat := range response.Container.Stats {
|
||||||
stats = append(stats, docker.ContainerStat{
|
stats = append(stats, container.ContainerStat{
|
||||||
ID: stat.Id,
|
ID: stat.Id,
|
||||||
CPUPercent: stat.CpuPercent,
|
CPUPercent: stat.CpuPercent,
|
||||||
MemoryPercent: stat.MemoryPercent,
|
MemoryPercent: stat.MemoryPercent,
|
||||||
@@ -288,7 +288,7 @@ func (c *Client) FindContainer(ctx context.Context, containerID string) (docker.
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return docker.Container{
|
return container.Container{
|
||||||
ID: response.Container.Id,
|
ID: response.Container.Id,
|
||||||
Name: response.Container.Name,
|
Name: response.Container.Name,
|
||||||
Image: response.Container.Image,
|
Image: response.Container.Image,
|
||||||
@@ -306,7 +306,7 @@ func (c *Client) FindContainer(ctx context.Context, containerID string) (docker.
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) ListContainers(ctx context.Context, filter docker.ContainerFilter) ([]docker.Container, error) {
|
func (c *Client) ListContainers(ctx context.Context, filter container.ContainerFilter) ([]container.Container, error) {
|
||||||
in := &pb.ListContainersRequest{}
|
in := &pb.ListContainersRequest{}
|
||||||
|
|
||||||
if filter != nil {
|
if filter != nil {
|
||||||
@@ -321,11 +321,11 @@ func (c *Client) ListContainers(ctx context.Context, filter docker.ContainerFilt
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
containers := make([]docker.Container, 0)
|
containers := make([]container.Container, 0)
|
||||||
for _, container := range response.Containers {
|
for _, c := range response.Containers {
|
||||||
var stats []docker.ContainerStat
|
var stats []container.ContainerStat
|
||||||
for _, stat := range container.Stats {
|
for _, stat := range c.Stats {
|
||||||
stats = append(stats, docker.ContainerStat{
|
stats = append(stats, container.ContainerStat{
|
||||||
ID: stat.Id,
|
ID: stat.Id,
|
||||||
CPUPercent: stat.CpuPercent,
|
CPUPercent: stat.CpuPercent,
|
||||||
MemoryPercent: stat.MemoryPercent,
|
MemoryPercent: stat.MemoryPercent,
|
||||||
@@ -333,20 +333,20 @@ func (c *Client) ListContainers(ctx context.Context, filter docker.ContainerFilt
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
containers = append(containers, docker.Container{
|
containers = append(containers, container.Container{
|
||||||
ID: container.Id,
|
ID: c.Id,
|
||||||
Name: container.Name,
|
Name: c.Name,
|
||||||
Image: container.Image,
|
Image: c.Image,
|
||||||
Labels: container.Labels,
|
Labels: c.Labels,
|
||||||
Group: container.Group,
|
Group: c.Group,
|
||||||
Created: container.Created.AsTime(),
|
Created: c.Created.AsTime(),
|
||||||
State: container.State,
|
State: c.State,
|
||||||
Health: container.Health,
|
Health: c.Health,
|
||||||
Host: container.Host,
|
Host: c.Host,
|
||||||
Tty: container.Tty,
|
Tty: c.Tty,
|
||||||
Command: container.Command,
|
Command: c.Command,
|
||||||
StartedAt: container.Started.AsTime(),
|
StartedAt: c.Started.AsTime(),
|
||||||
FinishedAt: container.Finished.AsTime(),
|
FinishedAt: c.Finished.AsTime(),
|
||||||
Stats: utils.RingBufferFrom(300, stats),
|
Stats: utils.RingBufferFrom(300, stats),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -354,17 +354,17 @@ func (c *Client) ListContainers(ctx context.Context, filter docker.ContainerFilt
|
|||||||
return containers, nil
|
return containers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Host(ctx context.Context) (docker.Host, error) {
|
func (c *Client) Host(ctx context.Context) (container.Host, error) {
|
||||||
info, err := c.client.HostInfo(ctx, &pb.HostInfoRequest{})
|
info, err := c.client.HostInfo(ctx, &pb.HostInfoRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return docker.Host{
|
return container.Host{
|
||||||
Endpoint: c.endpoint,
|
Endpoint: c.endpoint,
|
||||||
Type: "agent",
|
Type: "agent",
|
||||||
Available: false,
|
Available: false,
|
||||||
}, err
|
}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return docker.Host{
|
return container.Host{
|
||||||
ID: info.Host.Id,
|
ID: info.Host.Id,
|
||||||
Name: info.Host.Name,
|
Name: info.Host.Name,
|
||||||
NCPU: int(info.Host.CpuCores),
|
NCPU: int(info.Host.CpuCores),
|
||||||
@@ -376,16 +376,16 @@ func (c *Client) Host(ctx context.Context) (docker.Host, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) ContainerAction(ctx context.Context, containerId string, action docker.ContainerAction) error {
|
func (c *Client) ContainerAction(ctx context.Context, containerId string, action container.ContainerAction) error {
|
||||||
var containerAction pb.ContainerAction
|
var containerAction pb.ContainerAction
|
||||||
switch action {
|
switch action {
|
||||||
case docker.Start:
|
case container.Start:
|
||||||
containerAction = pb.ContainerAction_Start
|
containerAction = pb.ContainerAction_Start
|
||||||
|
|
||||||
case docker.Stop:
|
case container.Stop:
|
||||||
containerAction = pb.ContainerAction_Stop
|
containerAction = pb.ContainerAction_Stop
|
||||||
|
|
||||||
case docker.Restart:
|
case container.Restart:
|
||||||
containerAction = pb.ContainerAction_Restart
|
containerAction = pb.ContainerAction_Restart
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/amir20/dozzle/internal/utils"
|
"github.com/amir20/dozzle/internal/utils"
|
||||||
"github.com/docker/docker/api/types/system"
|
"github.com/docker/docker/api/types/system"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -28,46 +28,46 @@ var client *MockedClient
|
|||||||
|
|
||||||
type MockedClient struct {
|
type MockedClient struct {
|
||||||
mock.Mock
|
mock.Mock
|
||||||
docker.Client
|
container.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) FindContainer(ctx context.Context, id string) (docker.Container, error) {
|
func (m *MockedClient) FindContainer(ctx context.Context, id string) (container.Container, error) {
|
||||||
args := m.Called(ctx, id)
|
args := m.Called(ctx, id)
|
||||||
return args.Get(0).(docker.Container), args.Error(1)
|
return args.Get(0).(container.Container), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) ContainerActions(ctx context.Context, action docker.ContainerAction, containerID string) error {
|
func (m *MockedClient) ContainerActions(ctx context.Context, action container.ContainerAction, containerID string) error {
|
||||||
args := m.Called(ctx, action, containerID)
|
args := m.Called(ctx, action, containerID)
|
||||||
return args.Error(0)
|
return args.Error(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) ContainerEvents(ctx context.Context, events chan<- docker.ContainerEvent) error {
|
func (m *MockedClient) ContainerEvents(ctx context.Context, events chan<- container.ContainerEvent) error {
|
||||||
args := m.Called(ctx, events)
|
args := m.Called(ctx, events)
|
||||||
return args.Error(0)
|
return args.Error(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) ListContainers(ctx context.Context, filter docker.ContainerFilter) ([]docker.Container, error) {
|
func (m *MockedClient) ListContainers(ctx context.Context, filter container.ContainerFilter) ([]container.Container, error) {
|
||||||
args := m.Called(ctx, filter)
|
args := m.Called(ctx, filter)
|
||||||
return args.Get(0).([]docker.Container), args.Error(1)
|
return args.Get(0).([]container.Container), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) ContainerLogs(ctx context.Context, id string, since time.Time, stdType docker.StdType) (io.ReadCloser, error) {
|
func (m *MockedClient) ContainerLogs(ctx context.Context, id string, since time.Time, stdType container.StdType) (io.ReadCloser, error) {
|
||||||
args := m.Called(ctx, id, since, stdType)
|
args := m.Called(ctx, id, since, stdType)
|
||||||
return args.Get(0).(io.ReadCloser), args.Error(1)
|
return args.Get(0).(io.ReadCloser), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) ContainerStats(context.Context, string, chan<- docker.ContainerStat) error {
|
func (m *MockedClient) ContainerStats(context.Context, string, chan<- container.ContainerStat) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) ContainerLogsBetweenDates(ctx context.Context, id string, from time.Time, to time.Time, stdType docker.StdType) (io.ReadCloser, error) {
|
func (m *MockedClient) ContainerLogsBetweenDates(ctx context.Context, id string, from time.Time, to time.Time, stdType container.StdType) (io.ReadCloser, error) {
|
||||||
args := m.Called(ctx, id, from, to, stdType)
|
args := m.Called(ctx, id, from, to, stdType)
|
||||||
return args.Get(0).(io.ReadCloser), args.Error(1)
|
return args.Get(0).(io.ReadCloser), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) Host() docker.Host {
|
func (m *MockedClient) Host() container.Host {
|
||||||
args := m.Called()
|
args := m.Called()
|
||||||
return args.Get(0).(docker.Host)
|
return args.Get(0).(container.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) IsSwarmMode() bool {
|
func (m *MockedClient) IsSwarmMode() bool {
|
||||||
@@ -92,7 +92,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
client = &MockedClient{}
|
client = &MockedClient{}
|
||||||
client.On("ListContainers", mock.Anything, mock.Anything).Return([]docker.Container{
|
client.On("ListContainers", mock.Anything, mock.Anything).Return([]container.Container{
|
||||||
{
|
{
|
||||||
ID: "123456",
|
ID: "123456",
|
||||||
Name: "test",
|
Name: "test",
|
||||||
@@ -101,17 +101,17 @@ func init() {
|
|||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
client.On("Host").Return(docker.Host{
|
client.On("Host").Return(container.Host{
|
||||||
ID: "localhost",
|
ID: "localhost",
|
||||||
Endpoint: "local",
|
Endpoint: "local",
|
||||||
Name: "local",
|
Name: "local",
|
||||||
})
|
})
|
||||||
|
|
||||||
client.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(nil).Run(func(args mock.Arguments) {
|
client.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- container.ContainerEvent")).Return(nil).Run(func(args mock.Arguments) {
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
})
|
})
|
||||||
|
|
||||||
client.On("FindContainer", mock.Anything, "123456").Return(docker.Container{
|
client.On("FindContainer", mock.Anything, "123456").Return(container.Container{
|
||||||
ID: "123456",
|
ID: "123456",
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Host: "localhost",
|
Host: "localhost",
|
||||||
@@ -124,13 +124,13 @@ func init() {
|
|||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
"test": "test",
|
"test": "test",
|
||||||
},
|
},
|
||||||
Stats: utils.NewRingBuffer[docker.ContainerStat](300),
|
Stats: utils.NewRingBuffer[container.ContainerStat](300),
|
||||||
Created: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
Created: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||||
StartedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
StartedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||||
FinishedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
FinishedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
server, _ := NewServer(client, certs, "test", docker.ContainerFilter{})
|
server, _ := NewServer(client, certs, "test", container.ContainerFilter{})
|
||||||
|
|
||||||
go server.Serve(lis)
|
go server.Serve(lis)
|
||||||
}
|
}
|
||||||
@@ -145,9 +145,9 @@ func TestFindContainer(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
container, _ := rpc.FindContainer(context.Background(), "123456")
|
c, _ := rpc.FindContainer(context.Background(), "123456")
|
||||||
|
|
||||||
assert.Equal(t, container, docker.Container{
|
assert.Equal(t, c, container.Container{
|
||||||
ID: "123456",
|
ID: "123456",
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Host: "localhost",
|
Host: "localhost",
|
||||||
@@ -160,7 +160,7 @@ func TestFindContainer(t *testing.T) {
|
|||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
"test": "test",
|
"test": "test",
|
||||||
},
|
},
|
||||||
Stats: utils.NewRingBuffer[docker.ContainerStat](300),
|
Stats: utils.NewRingBuffer[container.ContainerStat](300),
|
||||||
Created: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
Created: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||||
StartedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
StartedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||||
FinishedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
FinishedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||||
@@ -173,9 +173,9 @@ func TestListContainers(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
containers, _ := rpc.ListContainers(context.Background(), docker.ContainerFilter{})
|
containers, _ := rpc.ListContainers(context.Background(), container.ContainerFilter{})
|
||||||
|
|
||||||
assert.Equal(t, containers, []docker.Container{
|
assert.Equal(t, containers, []container.Container{
|
||||||
{
|
{
|
||||||
ID: "123456",
|
ID: "123456",
|
||||||
Name: "test",
|
Name: "test",
|
||||||
@@ -189,7 +189,7 @@ func TestListContainers(t *testing.T) {
|
|||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
"test": "test",
|
"test": "test",
|
||||||
},
|
},
|
||||||
Stats: utils.NewRingBuffer[docker.ContainerStat](300),
|
Stats: utils.NewRingBuffer[container.ContainerStat](300),
|
||||||
Created: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
Created: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||||
StartedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
StartedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||||
FinishedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
FinishedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/agent/pb"
|
"github.com/amir20/dozzle/internal/agent/pb"
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
orderedmap "github.com/wk8/go-ordered-map/v2"
|
orderedmap "github.com/wk8/go-ordered-map/v2"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
@@ -25,19 +25,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type server struct {
|
type server struct {
|
||||||
client docker.Client
|
client container.Client
|
||||||
store *docker.ContainerStore
|
store *container.ContainerStore
|
||||||
version string
|
version string
|
||||||
|
|
||||||
pb.UnimplementedAgentServiceServer
|
pb.UnimplementedAgentServiceServer
|
||||||
}
|
}
|
||||||
|
|
||||||
func newServer(client docker.Client, dozzleVersion string, filter docker.ContainerFilter) pb.AgentServiceServer {
|
func newServer(client container.Client, dozzleVersion string, filter container.ContainerFilter) pb.AgentServiceServer {
|
||||||
return &server{
|
return &server{
|
||||||
client: client,
|
client: client,
|
||||||
version: dozzleVersion,
|
version: dozzleVersion,
|
||||||
|
|
||||||
store: docker.NewContainerStore(context.Background(), client, filter),
|
store: container.NewContainerStore(context.Background(), client, filter),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,17 +47,17 @@ func (s *server) StreamLogs(in *pb.StreamLogsRequest, out pb.AgentService_Stream
|
|||||||
since = in.Since.AsTime()
|
since = in.Since.AsTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
container, err := s.store.FindContainer(in.ContainerId, docker.ContainerFilter{})
|
c, err := s.store.FindContainer(in.ContainerId, container.ContainerFilter{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
reader, err := s.client.ContainerLogs(out.Context(), in.ContainerId, since, docker.StdType(in.StreamTypes))
|
reader, err := s.client.ContainerLogs(out.Context(), in.ContainerId, since, container.StdType(in.StreamTypes))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
g := docker.NewEventGenerator(out.Context(), reader, container)
|
g := container.NewEventGenerator(out.Context(), reader, c)
|
||||||
|
|
||||||
for event := range g.Events {
|
for event := range g.Events {
|
||||||
out.Send(&pb.StreamLogsResponse{
|
out.Send(&pb.StreamLogsResponse{
|
||||||
@@ -74,17 +74,17 @@ func (s *server) StreamLogs(in *pb.StreamLogsRequest, out pb.AgentService_Stream
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) LogsBetweenDates(in *pb.LogsBetweenDatesRequest, out pb.AgentService_LogsBetweenDatesServer) error {
|
func (s *server) LogsBetweenDates(in *pb.LogsBetweenDatesRequest, out pb.AgentService_LogsBetweenDatesServer) error {
|
||||||
reader, err := s.client.ContainerLogsBetweenDates(out.Context(), in.ContainerId, in.Since.AsTime(), in.Until.AsTime(), docker.StdType(in.StreamTypes))
|
reader, err := s.client.ContainerLogsBetweenDates(out.Context(), in.ContainerId, in.Since.AsTime(), in.Until.AsTime(), container.StdType(in.StreamTypes))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
container, err := s.client.FindContainer(out.Context(), in.ContainerId)
|
c, err := s.client.FindContainer(out.Context(), in.ContainerId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
g := docker.NewEventGenerator(out.Context(), reader, container)
|
g := container.NewEventGenerator(out.Context(), reader, c)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@@ -101,7 +101,7 @@ func (s *server) LogsBetweenDates(in *pb.LogsBetweenDatesRequest, out pb.AgentSe
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) StreamRawBytes(in *pb.StreamRawBytesRequest, out pb.AgentService_StreamRawBytesServer) error {
|
func (s *server) StreamRawBytes(in *pb.StreamRawBytesRequest, out pb.AgentService_StreamRawBytesServer) error {
|
||||||
reader, err := s.client.ContainerLogsBetweenDates(out.Context(), in.ContainerId, in.Since.AsTime(), in.Until.AsTime(), docker.StdType(in.StreamTypes))
|
reader, err := s.client.ContainerLogsBetweenDates(out.Context(), in.ContainerId, in.Since.AsTime(), in.Until.AsTime(), container.StdType(in.StreamTypes))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -129,7 +129,7 @@ func (s *server) StreamRawBytes(in *pb.StreamRawBytesRequest, out pb.AgentServic
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) StreamEvents(in *pb.StreamEventsRequest, out pb.AgentService_StreamEventsServer) error {
|
func (s *server) StreamEvents(in *pb.StreamEventsRequest, out pb.AgentService_StreamEventsServer) error {
|
||||||
events := make(chan docker.ContainerEvent)
|
events := make(chan container.ContainerEvent)
|
||||||
|
|
||||||
s.store.SubscribeEvents(out.Context(), events)
|
s.store.SubscribeEvents(out.Context(), events)
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ func (s *server) StreamEvents(in *pb.StreamEventsRequest, out pb.AgentService_St
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) StreamStats(in *pb.StreamStatsRequest, out pb.AgentService_StreamStatsServer) error {
|
func (s *server) StreamStats(in *pb.StreamStatsRequest, out pb.AgentService_StreamStatsServer) error {
|
||||||
stats := make(chan docker.ContainerStat)
|
stats := make(chan container.ContainerStat)
|
||||||
|
|
||||||
s.store.SubscribeStats(out.Context(), stats)
|
s.store.SubscribeStats(out.Context(), stats)
|
||||||
|
|
||||||
@@ -173,7 +173,7 @@ func (s *server) StreamStats(in *pb.StreamStatsRequest, out pb.AgentService_Stre
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) FindContainer(ctx context.Context, in *pb.FindContainerRequest) (*pb.FindContainerResponse, error) {
|
func (s *server) FindContainer(ctx context.Context, in *pb.FindContainerRequest) (*pb.FindContainerResponse, error) {
|
||||||
filter := make(docker.ContainerFilter)
|
filter := make(container.ContainerFilter)
|
||||||
if in.GetFilter() != nil {
|
if in.GetFilter() != nil {
|
||||||
for k, v := range in.GetFilter() {
|
for k, v := range in.GetFilter() {
|
||||||
filter[k] = append(filter[k], v.GetValues()...)
|
filter[k] = append(filter[k], v.GetValues()...)
|
||||||
@@ -205,7 +205,7 @@ func (s *server) FindContainer(ctx context.Context, in *pb.FindContainerRequest)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) ListContainers(ctx context.Context, in *pb.ListContainersRequest) (*pb.ListContainersResponse, error) {
|
func (s *server) ListContainers(ctx context.Context, in *pb.ListContainersRequest) (*pb.ListContainersResponse, error) {
|
||||||
filter := make(docker.ContainerFilter)
|
filter := make(container.ContainerFilter)
|
||||||
if in.GetFilter() != nil {
|
if in.GetFilter() != nil {
|
||||||
for k, v := range in.GetFilter() {
|
for k, v := range in.GetFilter() {
|
||||||
filter[k] = append(filter[k], v.GetValues()...)
|
filter[k] = append(filter[k], v.GetValues()...)
|
||||||
@@ -268,7 +268,7 @@ func (s *server) HostInfo(ctx context.Context, in *pb.HostInfoRequest) (*pb.Host
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) StreamContainerStarted(in *pb.StreamContainerStartedRequest, out pb.AgentService_StreamContainerStartedServer) error {
|
func (s *server) StreamContainerStarted(in *pb.StreamContainerStartedRequest, out pb.AgentService_StreamContainerStartedServer) error {
|
||||||
containers := make(chan docker.Container)
|
containers := make(chan container.Container)
|
||||||
|
|
||||||
go s.store.SubscribeNewContainers(out.Context(), containers)
|
go s.store.SubscribeNewContainers(out.Context(), containers)
|
||||||
|
|
||||||
@@ -298,16 +298,16 @@ func (s *server) StreamContainerStarted(in *pb.StreamContainerStartedRequest, ou
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) ContainerAction(ctx context.Context, in *pb.ContainerActionRequest) (*pb.ContainerActionResponse, error) {
|
func (s *server) ContainerAction(ctx context.Context, in *pb.ContainerActionRequest) (*pb.ContainerActionResponse, error) {
|
||||||
var action docker.ContainerAction
|
var action container.ContainerAction
|
||||||
switch in.Action {
|
switch in.Action {
|
||||||
case pb.ContainerAction_Start:
|
case pb.ContainerAction_Start:
|
||||||
action = docker.Start
|
action = container.Start
|
||||||
|
|
||||||
case pb.ContainerAction_Stop:
|
case pb.ContainerAction_Stop:
|
||||||
action = docker.Stop
|
action = container.Stop
|
||||||
|
|
||||||
case pb.ContainerAction_Restart:
|
case pb.ContainerAction_Restart:
|
||||||
action = docker.Restart
|
action = container.Restart
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, status.Error(codes.InvalidArgument, "invalid action")
|
return nil, status.Error(codes.InvalidArgument, "invalid action")
|
||||||
@@ -322,7 +322,7 @@ func (s *server) ContainerAction(ctx context.Context, in *pb.ContainerActionRequ
|
|||||||
return &pb.ContainerActionResponse{}, nil
|
return &pb.ContainerActionResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(client docker.Client, certificates tls.Certificate, dozzleVersion string, filter docker.ContainerFilter) (*grpc.Server, error) {
|
func NewServer(client container.Client, certificates tls.Certificate, dozzleVersion string, filter container.ContainerFilter) (*grpc.Server, error) {
|
||||||
caCertPool := x509.NewCertPool()
|
caCertPool := x509.NewCertPool()
|
||||||
c, err := x509.ParseCertificate(certificates.Certificate[0])
|
c, err := x509.ParseCertificate(certificates.Certificate[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -346,7 +346,7 @@ func NewServer(client docker.Client, certificates tls.Certificate, dozzleVersion
|
|||||||
return grpcServer, nil
|
return grpcServer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func logEventToPb(event *docker.LogEvent) *pb.LogEvent {
|
func logEventToPb(event *container.LogEvent) *pb.LogEvent {
|
||||||
var message *anypb.Any
|
var message *anypb.Any
|
||||||
|
|
||||||
if event.Message == nil {
|
if event.Message == nil {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ func NewForwardProxyAuth(userHeader, emailHeader, nameHeader, filterHeader strin
|
|||||||
func (p *proxyAuthContext) AuthMiddleware(next http.Handler) http.Handler {
|
func (p *proxyAuthContext) AuthMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Header.Get(p.headerUser) != "" {
|
if r.Header.Get(p.headerUser) != "" {
|
||||||
containerFilter, err := docker.ParseContainerFilter(r.Header.Get(p.headerFilter))
|
containerFilter, err := container.ParseContainerFilter(r.Header.Get(p.headerFilter))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Str("filter", r.Header.Get(p.headerFilter)).Msg("Failed to parse container filter")
|
log.Fatal().Str("filter", r.Header.Get(p.headerFilter)).Msg("Failed to parse container filter")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/go-chi/jwtauth/v5"
|
"github.com/go-chi/jwtauth/v5"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
@@ -24,7 +24,7 @@ type User struct {
|
|||||||
Name string `json:"name" yaml:"name"`
|
Name string `json:"name" yaml:"name"`
|
||||||
Password string `json:"-" yaml:"password"`
|
Password string `json:"-" yaml:"password"`
|
||||||
Filter string `json:"-" yaml:"filter"`
|
Filter string `json:"-" yaml:"filter"`
|
||||||
ContainerFilter docker.ContainerFilter `json:"-" yaml:"-"`
|
ContainerFilter container.ContainerFilter `json:"-" yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) AvatarURL() string {
|
func (u User) AvatarURL() string {
|
||||||
@@ -35,7 +35,7 @@ func (u User) AvatarURL() string {
|
|||||||
return fmt.Sprintf("https://gravatar.com/avatar/%s?d=https%%3A%%2F%%2Fui-avatars.com%%2Fapi%%2F/%s/128", hashEmail(u.Email), url.QueryEscape(name))
|
return fmt.Sprintf("https://gravatar.com/avatar/%s?d=https%%3A%%2F%%2Fui-avatars.com%%2Fapi%%2F/%s/128", hashEmail(u.Email), url.QueryEscape(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUser(username, email, name string, filter docker.ContainerFilter) User {
|
func newUser(username, email, name string, filter container.ContainerFilter) User {
|
||||||
return User{
|
return User{
|
||||||
Username: username,
|
Username: username,
|
||||||
Email: email,
|
Email: email,
|
||||||
@@ -197,9 +197,9 @@ func UserFromContext(ctx context.Context) *User {
|
|||||||
}
|
}
|
||||||
email := claims["email"].(string)
|
email := claims["email"].(string)
|
||||||
name := claims["name"].(string)
|
name := claims["name"].(string)
|
||||||
containerFilter := docker.ContainerFilter{}
|
containerFilter := container.ContainerFilter{}
|
||||||
if filter, ok := claims["filter"].(string); ok {
|
if filter, ok := claims["filter"].(string); ok {
|
||||||
containerFilter, err = docker.ParseContainerFilter(filter)
|
containerFilter, err = container.ParseContainerFilter(filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Str("filter", filter).Msg("Failed to parse container filter")
|
log.Fatal().Err(err).Str("filter", filter).Msg("Failed to parse container filter")
|
||||||
}
|
}
|
||||||
|
|||||||
42
internal/container/client.go
Normal file
42
internal/container/client.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StdType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
UNKNOWN StdType = 1 << iota
|
||||||
|
STDOUT
|
||||||
|
STDERR
|
||||||
|
)
|
||||||
|
const STDALL = STDOUT | STDERR
|
||||||
|
|
||||||
|
func (s StdType) String() string {
|
||||||
|
switch s {
|
||||||
|
case STDOUT:
|
||||||
|
return "stdout"
|
||||||
|
case STDERR:
|
||||||
|
return "stderr"
|
||||||
|
case STDALL:
|
||||||
|
return "all"
|
||||||
|
default:
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client interface {
|
||||||
|
ListContainers(context.Context, ContainerFilter) ([]Container, error)
|
||||||
|
FindContainer(context.Context, string) (Container, error)
|
||||||
|
ContainerLogs(context.Context, string, time.Time, StdType) (io.ReadCloser, error)
|
||||||
|
ContainerEvents(context.Context, chan<- ContainerEvent) error
|
||||||
|
ContainerLogsBetweenDates(context.Context, string, time.Time, time.Time, StdType) (io.ReadCloser, error)
|
||||||
|
ContainerStats(context.Context, string, chan<- ContainerStat) error
|
||||||
|
Ping(context.Context) error
|
||||||
|
Host() Host
|
||||||
|
ContainerActions(ctx context.Context, action ContainerAction, containerID string) error
|
||||||
|
IsSwarmMode() bool
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package docker
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package docker
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -48,7 +48,7 @@ func TestContainerStore_List(t *testing.T) {
|
|||||||
Name: "test",
|
Name: "test",
|
||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
client.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(nil).Run(func(args mock.Arguments) {
|
client.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- container.ContainerEvent")).Return(nil).Run(func(args mock.Arguments) {
|
||||||
ctx := args.Get(0).(context.Context)
|
ctx := args.Get(0).(context.Context)
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
})
|
})
|
||||||
@@ -83,7 +83,7 @@ func TestContainerStore_die(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
client.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(nil).
|
client.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- container.ContainerEvent")).Return(nil).
|
||||||
Run(func(args mock.Arguments) {
|
Run(func(args mock.Arguments) {
|
||||||
ctx := args.Get(0).(context.Context)
|
ctx := args.Get(0).(context.Context)
|
||||||
events := args.Get(1).(chan<- ContainerEvent)
|
events := args.Get(1).(chan<- ContainerEvent)
|
||||||
@@ -98,7 +98,7 @@ func TestContainerStore_die(t *testing.T) {
|
|||||||
ID: "localhost",
|
ID: "localhost",
|
||||||
})
|
})
|
||||||
|
|
||||||
client.On("ContainerStats", mock.Anything, "1234", mock.AnythingOfType("chan<- docker.ContainerStat")).Return(nil)
|
client.On("ContainerStats", mock.Anything, "1234", mock.AnythingOfType("chan<- container.ContainerStat")).Return(nil)
|
||||||
|
|
||||||
client.On("FindContainer", mock.Anything, "1234").Return(Container{
|
client.On("FindContainer", mock.Anything, "1234").Return(Container{
|
||||||
ID: "1234",
|
ID: "1234",
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package docker
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"html"
|
"html"
|
||||||
@@ -30,7 +30,6 @@ func escape(logEvent *LogEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func escapeAnyMap(orderedMap *orderedmap.OrderedMap[string, any]) {
|
func escapeAnyMap(orderedMap *orderedmap.OrderedMap[string, any]) {
|
||||||
|
|
||||||
for pair := orderedMap.Oldest(); pair != nil; pair = pair.Next() {
|
for pair := orderedMap.Oldest(); pair != nil; pair = pair.Next() {
|
||||||
switch value := pair.Value.(type) {
|
switch value := pair.Value.(type) {
|
||||||
case string:
|
case string:
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package docker
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package docker
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package docker
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -25,6 +25,7 @@ type Host struct {
|
|||||||
AgentVersion string `json:"agentVersion,omitempty"`
|
AgentVersion string `json:"agentVersion,omitempty"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Available bool `json:"available"`
|
Available bool `json:"available"`
|
||||||
|
Swarm bool `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h Host) String() string {
|
func (h Host) String() string {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package docker
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package docker
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package docker
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package docker
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package docker
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package docker
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -17,13 +17,13 @@ func startedCollector(ctx context.Context) *StatsCollector {
|
|||||||
State: "running",
|
State: "running",
|
||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
client.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).
|
client.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- container.ContainerEvent")).
|
||||||
Return(nil).
|
Return(nil).
|
||||||
Run(func(args mock.Arguments) {
|
Run(func(args mock.Arguments) {
|
||||||
ctx := args.Get(0).(context.Context)
|
ctx := args.Get(0).(context.Context)
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
})
|
})
|
||||||
client.On("ContainerStats", mock.Anything, mock.Anything, mock.AnythingOfType("chan<- docker.ContainerStat")).
|
client.On("ContainerStats", mock.Anything, mock.Anything, mock.AnythingOfType("chan<- container.ContainerStat")).
|
||||||
Return(nil).
|
Return(nil).
|
||||||
Run(func(args mock.Arguments) {
|
Run(func(args mock.Arguments) {
|
||||||
stats := args.Get(2).(chan<- ContainerStat)
|
stats := args.Get(2).(chan<- ContainerStat)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package docker
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package docker
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/utils"
|
"github.com/amir20/dozzle/internal/utils"
|
||||||
"github.com/docker/docker/api/types/filters"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Container represents an internal representation of docker containers
|
// Container represents an internal representation of docker containers
|
||||||
@@ -70,17 +69,6 @@ func (f ContainerFilter) Exists() bool {
|
|||||||
return len(f) > 0
|
return len(f) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f ContainerFilter) asArgs() filters.Args {
|
|
||||||
filterArgs := filters.NewArgs()
|
|
||||||
for key, values := range f {
|
|
||||||
for _, value := range values {
|
|
||||||
filterArgs.Add(key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return filterArgs
|
|
||||||
}
|
|
||||||
|
|
||||||
type LogPosition string
|
type LogPosition string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -11,10 +11,12 @@ import (
|
|||||||
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/amir20/dozzle/internal/utils"
|
"github.com/amir20/dozzle/internal/utils"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/container"
|
docker "github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/events"
|
"github.com/docker/docker/api/types/events"
|
||||||
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/docker/docker/api/types/system"
|
"github.com/docker/docker/api/types/system"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
@@ -22,70 +24,41 @@ import (
|
|||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StdType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
UNKNOWN StdType = 1 << iota
|
|
||||||
STDOUT
|
|
||||||
STDERR
|
|
||||||
)
|
|
||||||
const STDALL = STDOUT | STDERR
|
|
||||||
|
|
||||||
func (s StdType) String() string {
|
|
||||||
switch s {
|
|
||||||
case STDOUT:
|
|
||||||
return "stdout"
|
|
||||||
case STDERR:
|
|
||||||
return "stderr"
|
|
||||||
case STDALL:
|
|
||||||
return "all"
|
|
||||||
default:
|
|
||||||
return "unknown"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type DockerCLI interface {
|
type DockerCLI interface {
|
||||||
ContainerList(context.Context, container.ListOptions) ([]types.Container, error)
|
ContainerList(context.Context, docker.ListOptions) ([]types.Container, error)
|
||||||
ContainerLogs(context.Context, string, container.LogsOptions) (io.ReadCloser, error)
|
ContainerLogs(context.Context, string, docker.LogsOptions) (io.ReadCloser, error)
|
||||||
Events(context.Context, events.ListOptions) (<-chan events.Message, <-chan error)
|
Events(context.Context, events.ListOptions) (<-chan events.Message, <-chan error)
|
||||||
ContainerInspect(ctx context.Context, containerID string) (types.ContainerJSON, error)
|
ContainerInspect(ctx context.Context, containerID string) (types.ContainerJSON, error)
|
||||||
ContainerStats(ctx context.Context, containerID string, stream bool) (container.StatsResponseReader, error)
|
ContainerStats(ctx context.Context, containerID string, stream bool) (docker.StatsResponseReader, error)
|
||||||
Ping(ctx context.Context) (types.Ping, error)
|
Ping(ctx context.Context) (types.Ping, error)
|
||||||
ContainerStart(ctx context.Context, containerID string, options container.StartOptions) error
|
ContainerStart(ctx context.Context, containerID string, options docker.StartOptions) error
|
||||||
ContainerStop(ctx context.Context, containerID string, options container.StopOptions) error
|
ContainerStop(ctx context.Context, containerID string, options docker.StopOptions) error
|
||||||
ContainerRestart(ctx context.Context, containerID string, options container.StopOptions) error
|
ContainerRestart(ctx context.Context, containerID string, options docker.StopOptions) error
|
||||||
Info(ctx context.Context) (system.Info, error)
|
Info(ctx context.Context) (system.Info, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Client interface {
|
|
||||||
ListContainers(context.Context, ContainerFilter) ([]Container, error)
|
|
||||||
FindContainer(context.Context, string) (Container, error)
|
|
||||||
ContainerLogs(context.Context, string, time.Time, StdType) (io.ReadCloser, error)
|
|
||||||
ContainerEvents(context.Context, chan<- ContainerEvent) 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
|
|
||||||
ContainerActions(ctx context.Context, action ContainerAction, containerID string) error
|
|
||||||
IsSwarmMode() bool
|
|
||||||
SystemInfo() system.Info
|
|
||||||
}
|
|
||||||
|
|
||||||
type httpClient struct {
|
type httpClient struct {
|
||||||
cli DockerCLI
|
cli DockerCLI
|
||||||
host Host
|
host container.Host
|
||||||
info system.Info
|
info system.Info
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(cli DockerCLI, host Host) Client {
|
func NewClient(cli DockerCLI, host container.Host) container.Client {
|
||||||
info, err := cli.Info(context.Background())
|
info, err := cli.Info(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Failed to get docker info")
|
log.Error().Err(err).Msg("Failed to get docker info")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id := info.ID
|
||||||
|
if info.Swarm.NodeID != "" {
|
||||||
|
id = info.Swarm.NodeID
|
||||||
|
}
|
||||||
|
|
||||||
|
host.ID = id
|
||||||
host.NCPU = info.NCPU
|
host.NCPU = info.NCPU
|
||||||
host.MemTotal = info.MemTotal
|
host.MemTotal = info.MemTotal
|
||||||
host.DockerVersion = info.ServerVersion
|
host.DockerVersion = info.ServerVersion
|
||||||
|
host.Swarm = info.Swarm.NodeID != ""
|
||||||
|
|
||||||
return &httpClient{
|
return &httpClient{
|
||||||
cli: cli,
|
cli: cli,
|
||||||
@@ -95,7 +68,7 @@ func NewClient(cli DockerCLI, host Host) Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewClientWithFilters creates a new instance of Client with docker filters
|
// NewClientWithFilters creates a new instance of Client with docker filters
|
||||||
func NewLocalClient(hostname string) (Client, error) {
|
func NewLocalClient(hostname string) (container.Client, error) {
|
||||||
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -107,16 +80,8 @@ func NewLocalClient(hostname string) (Client, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
id := info.ID
|
host := container.Host{
|
||||||
if info.Swarm.NodeID != "" {
|
|
||||||
id = info.Swarm.NodeID
|
|
||||||
}
|
|
||||||
|
|
||||||
host := Host{
|
|
||||||
ID: id,
|
|
||||||
Name: info.Name,
|
Name: info.Name,
|
||||||
MemTotal: info.MemTotal,
|
|
||||||
NCPU: info.NCPU,
|
|
||||||
Endpoint: "local",
|
Endpoint: "local",
|
||||||
Type: "local",
|
Type: "local",
|
||||||
}
|
}
|
||||||
@@ -128,7 +93,7 @@ func NewLocalClient(hostname string) (Client, error) {
|
|||||||
return NewClient(cli, host), nil
|
return NewClient(cli, host), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRemoteClient(host Host) (Client, error) {
|
func NewRemoteClient(host container.Host) (container.Client, error) {
|
||||||
if host.URL.Scheme != "tcp" {
|
if host.URL.Scheme != "tcp" {
|
||||||
return nil, fmt.Errorf("invalid scheme: %s", host.URL.Scheme)
|
return nil, fmt.Errorf("invalid scheme: %s", host.URL.Scheme)
|
||||||
}
|
}
|
||||||
@@ -158,33 +123,39 @@ func NewRemoteClient(host Host) (Client, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Finds a container by id, skipping the filters
|
// Finds a container by id, skipping the filters
|
||||||
func (d *httpClient) FindContainer(ctx context.Context, id string) (Container, error) {
|
func (d *httpClient) FindContainer(ctx context.Context, id string) (container.Container, error) {
|
||||||
log.Debug().Str("id", id).Msg("Finding container")
|
log.Debug().Str("id", id).Msg("Finding container")
|
||||||
if json, err := d.cli.ContainerInspect(ctx, id); err == nil {
|
if json, err := d.cli.ContainerInspect(ctx, id); err == nil {
|
||||||
return newContainerFromJSON(json, d.host.ID), nil
|
return newContainerFromJSON(json, d.host.ID), nil
|
||||||
} else {
|
} else {
|
||||||
return Container{}, err
|
return container.Container{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *httpClient) ContainerActions(ctx context.Context, action ContainerAction, containerID string) error {
|
func (d *httpClient) ContainerActions(ctx context.Context, action container.ContainerAction, containerID string) error {
|
||||||
switch action {
|
switch action {
|
||||||
case Start:
|
case container.Start:
|
||||||
return d.cli.ContainerStart(ctx, containerID, container.StartOptions{})
|
return d.cli.ContainerStart(ctx, containerID, docker.StartOptions{})
|
||||||
case Stop:
|
case container.Stop:
|
||||||
return d.cli.ContainerStop(ctx, containerID, container.StopOptions{})
|
return d.cli.ContainerStop(ctx, containerID, docker.StopOptions{})
|
||||||
case Restart:
|
case container.Restart:
|
||||||
return d.cli.ContainerRestart(ctx, containerID, container.StopOptions{})
|
return d.cli.ContainerRestart(ctx, containerID, docker.StopOptions{})
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown action: %s", action)
|
return fmt.Errorf("unknown action: %s", action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *httpClient) ListContainers(ctx context.Context, filter ContainerFilter) ([]Container, error) {
|
func (d *httpClient) ListContainers(ctx context.Context, filter container.ContainerFilter) ([]container.Container, error) {
|
||||||
log.Debug().Interface("filter", filter).Str("host", d.host.Name).Msg("Listing containers")
|
log.Debug().Interface("filter", filter).Str("host", d.host.Name).Msg("Listing containers")
|
||||||
containerListOptions := container.ListOptions{
|
filterArgs := filters.NewArgs()
|
||||||
Filters: filter.asArgs(),
|
for key, values := range filter {
|
||||||
|
for _, value := range values {
|
||||||
|
filterArgs.Add(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
containerListOptions := docker.ListOptions{
|
||||||
|
Filters: filterArgs,
|
||||||
All: true,
|
All: true,
|
||||||
}
|
}
|
||||||
list, err := d.cli.ContainerList(ctx, containerListOptions)
|
list, err := d.cli.ContainerList(ctx, containerListOptions)
|
||||||
@@ -192,7 +163,7 @@ func (d *httpClient) ListContainers(ctx context.Context, filter ContainerFilter)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var containers = make([]Container, 0, len(list))
|
var containers = make([]container.Container, 0, len(list))
|
||||||
for _, c := range list {
|
for _, c := range list {
|
||||||
containers = append(containers, newContainer(c, d.host.ID))
|
containers = append(containers, newContainer(c, d.host.ID))
|
||||||
}
|
}
|
||||||
@@ -204,7 +175,7 @@ func (d *httpClient) ListContainers(ctx context.Context, filter ContainerFilter)
|
|||||||
return containers, nil
|
return containers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *httpClient) ContainerStats(ctx context.Context, id string, stats chan<- ContainerStat) error {
|
func (d *httpClient) ContainerStats(ctx context.Context, id string, stats chan<- container.ContainerStat) error {
|
||||||
response, err := d.cli.ContainerStats(ctx, id, true)
|
response, err := d.cli.ContainerStats(ctx, id, true)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -213,7 +184,7 @@ func (d *httpClient) ContainerStats(ctx context.Context, id string, stats chan<-
|
|||||||
|
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
decoder := json.NewDecoder(response.Body)
|
decoder := json.NewDecoder(response.Body)
|
||||||
var v *container.StatsResponse
|
var v *docker.StatsResponse
|
||||||
for {
|
for {
|
||||||
if err := decoder.Decode(&v); err != nil {
|
if err := decoder.Decode(&v); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -243,7 +214,7 @@ func (d *httpClient) ContainerStats(ctx context.Context, id string, stats chan<-
|
|||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return nil
|
return nil
|
||||||
case stats <- ContainerStat{
|
case stats <- container.ContainerStat{
|
||||||
ID: id,
|
ID: id,
|
||||||
CPUPercent: cpuPercent,
|
CPUPercent: cpuPercent,
|
||||||
MemoryPercent: memPercent,
|
MemoryPercent: memPercent,
|
||||||
@@ -254,13 +225,13 @@ func (d *httpClient) ContainerStats(ctx context.Context, id string, stats chan<-
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *httpClient) ContainerLogs(ctx context.Context, id string, since time.Time, stdType StdType) (io.ReadCloser, error) {
|
func (d *httpClient) ContainerLogs(ctx context.Context, id string, since time.Time, stdType container.StdType) (io.ReadCloser, error) {
|
||||||
log.Debug().Str("id", id).Time("since", since).Stringer("stdType", stdType).Str("host", d.host.Name).Msg("Streaming logs for container")
|
log.Debug().Str("id", id).Time("since", since).Stringer("stdType", stdType).Str("host", d.host.Name).Msg("Streaming logs for container")
|
||||||
|
|
||||||
sinceQuery := since.Add(-50 * time.Millisecond).Format(time.RFC3339Nano)
|
sinceQuery := since.Add(-50 * time.Millisecond).Format(time.RFC3339Nano)
|
||||||
options := container.LogsOptions{
|
options := docker.LogsOptions{
|
||||||
ShowStdout: stdType&STDOUT != 0,
|
ShowStdout: stdType&container.STDOUT != 0,
|
||||||
ShowStderr: stdType&STDERR != 0,
|
ShowStderr: stdType&container.STDERR != 0,
|
||||||
Follow: true,
|
Follow: true,
|
||||||
Tail: strconv.Itoa(100),
|
Tail: strconv.Itoa(100),
|
||||||
Timestamps: true,
|
Timestamps: true,
|
||||||
@@ -275,7 +246,7 @@ func (d *httpClient) ContainerLogs(ctx context.Context, id string, since time.Ti
|
|||||||
return reader, nil
|
return reader, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *httpClient) ContainerEvents(ctx context.Context, messages chan<- ContainerEvent) error {
|
func (d *httpClient) ContainerEvents(ctx context.Context, messages chan<- container.ContainerEvent) error {
|
||||||
dockerMessages, err := d.cli.Events(ctx, events.ListOptions{})
|
dockerMessages, err := d.cli.Events(ctx, events.ListOptions{})
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@@ -287,7 +258,7 @@ func (d *httpClient) ContainerEvents(ctx context.Context, messages chan<- Contai
|
|||||||
|
|
||||||
case message := <-dockerMessages:
|
case message := <-dockerMessages:
|
||||||
if message.Type == events.ContainerEventType && len(message.Actor.ID) > 0 {
|
if message.Type == events.ContainerEventType && len(message.Actor.ID) > 0 {
|
||||||
messages <- ContainerEvent{
|
messages <- container.ContainerEvent{
|
||||||
ActorID: message.Actor.ID[:12],
|
ActorID: message.Actor.ID[:12],
|
||||||
Name: string(message.Action),
|
Name: string(message.Action),
|
||||||
Host: d.host.ID,
|
Host: d.host.ID,
|
||||||
@@ -299,11 +270,11 @@ func (d *httpClient) ContainerEvents(ctx context.Context, messages chan<- Contai
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *httpClient) ContainerLogsBetweenDates(ctx context.Context, id string, from time.Time, to time.Time, stdType StdType) (io.ReadCloser, error) {
|
func (d *httpClient) ContainerLogsBetweenDates(ctx context.Context, id string, from time.Time, to time.Time, stdType container.StdType) (io.ReadCloser, error) {
|
||||||
log.Debug().Str("id", id).Time("from", from).Time("to", to).Stringer("stdType", stdType).Str("host", d.host.Name).Msg("Fetching logs between dates for container")
|
log.Debug().Str("id", id).Time("from", from).Time("to", to).Stringer("stdType", stdType).Str("host", d.host.Name).Msg("Fetching logs between dates for container")
|
||||||
options := container.LogsOptions{
|
options := docker.LogsOptions{
|
||||||
ShowStdout: stdType&STDOUT != 0,
|
ShowStdout: stdType&container.STDOUT != 0,
|
||||||
ShowStderr: stdType&STDERR != 0,
|
ShowStderr: stdType&container.STDERR != 0,
|
||||||
Timestamps: true,
|
Timestamps: true,
|
||||||
Since: from.Format(time.RFC3339Nano),
|
Since: from.Format(time.RFC3339Nano),
|
||||||
Until: to.Format(time.RFC3339Nano),
|
Until: to.Format(time.RFC3339Nano),
|
||||||
@@ -317,11 +288,12 @@ func (d *httpClient) ContainerLogsBetweenDates(ctx context.Context, id string, f
|
|||||||
return reader, nil
|
return reader, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *httpClient) Ping(ctx context.Context) (types.Ping, error) {
|
func (d *httpClient) Ping(ctx context.Context) error {
|
||||||
return d.cli.Ping(ctx)
|
_, err := d.cli.Ping(ctx)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *httpClient) Host() Host {
|
func (d *httpClient) Host() container.Host {
|
||||||
log.Debug().Str("host", d.host.Name).Msg("Fetching host")
|
log.Debug().Str("host", d.host.Name).Msg("Fetching host")
|
||||||
return d.host
|
return d.host
|
||||||
}
|
}
|
||||||
@@ -330,11 +302,7 @@ func (d *httpClient) IsSwarmMode() bool {
|
|||||||
return d.info.Swarm.LocalNodeState != swarm.LocalNodeStateInactive
|
return d.info.Swarm.LocalNodeState != swarm.LocalNodeStateInactive
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *httpClient) SystemInfo() system.Info {
|
func newContainer(c types.Container, host string) container.Container {
|
||||||
return d.info
|
|
||||||
}
|
|
||||||
|
|
||||||
func newContainer(c types.Container, host string) Container {
|
|
||||||
name := "no name"
|
name := "no name"
|
||||||
if c.Labels["dev.dozzle.name"] != "" {
|
if c.Labels["dev.dozzle.name"] != "" {
|
||||||
name = c.Labels["dev.dozzle.name"]
|
name = c.Labels["dev.dozzle.name"]
|
||||||
@@ -346,7 +314,7 @@ func newContainer(c types.Container, host string) Container {
|
|||||||
if c.Labels["dev.dozzle.group"] != "" {
|
if c.Labels["dev.dozzle.group"] != "" {
|
||||||
group = c.Labels["dev.dozzle.group"]
|
group = c.Labels["dev.dozzle.group"]
|
||||||
}
|
}
|
||||||
return Container{
|
return container.Container{
|
||||||
ID: c.ID[:12],
|
ID: c.ID[:12],
|
||||||
Name: name,
|
Name: name,
|
||||||
Image: c.Image,
|
Image: c.Image,
|
||||||
@@ -355,12 +323,12 @@ func newContainer(c types.Container, host string) Container {
|
|||||||
State: c.State,
|
State: c.State,
|
||||||
Host: host,
|
Host: host,
|
||||||
Labels: c.Labels,
|
Labels: c.Labels,
|
||||||
Stats: utils.NewRingBuffer[ContainerStat](300), // 300 seconds of stats
|
Stats: utils.NewRingBuffer[container.ContainerStat](300), // 300 seconds of stats
|
||||||
Group: group,
|
Group: group,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newContainerFromJSON(c types.ContainerJSON, host string) Container {
|
func newContainerFromJSON(c types.ContainerJSON, host string) container.Container {
|
||||||
name := "no name"
|
name := "no name"
|
||||||
if c.Config.Labels["dev.dozzle.name"] != "" {
|
if c.Config.Labels["dev.dozzle.name"] != "" {
|
||||||
name = c.Config.Labels["dev.dozzle.name"]
|
name = c.Config.Labels["dev.dozzle.name"]
|
||||||
@@ -373,7 +341,7 @@ func newContainerFromJSON(c types.ContainerJSON, host string) Container {
|
|||||||
group = c.Config.Labels["dev.dozzle.group"]
|
group = c.Config.Labels["dev.dozzle.group"]
|
||||||
}
|
}
|
||||||
|
|
||||||
container := Container{
|
container := container.Container{
|
||||||
ID: c.ID[:12],
|
ID: c.ID[:12],
|
||||||
Name: name,
|
Name: name,
|
||||||
Image: c.Config.Image,
|
Image: c.Config.Image,
|
||||||
@@ -381,7 +349,7 @@ func newContainerFromJSON(c types.ContainerJSON, host string) Container {
|
|||||||
State: c.State.Status,
|
State: c.State.Status,
|
||||||
Host: host,
|
Host: host,
|
||||||
Labels: c.Config.Labels,
|
Labels: c.Config.Labels,
|
||||||
Stats: utils.NewRingBuffer[ContainerStat](300), // 300 seconds of stats
|
Stats: utils.NewRingBuffer[container.ContainerStat](300), // 300 seconds of stats
|
||||||
Group: group,
|
Group: group,
|
||||||
Tty: c.Config.Tty,
|
Tty: c.Config.Tty,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ import (
|
|||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/container"
|
docker "github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/system"
|
"github.com/docker/docker/api/types/system"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
@@ -23,7 +24,7 @@ type mockedProxy struct {
|
|||||||
DockerCLI
|
DockerCLI
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockedProxy) ContainerList(context.Context, container.ListOptions) ([]types.Container, error) {
|
func (m *mockedProxy) ContainerList(context.Context, docker.ListOptions) ([]types.Container, error) {
|
||||||
args := m.Called()
|
args := m.Called()
|
||||||
containers, ok := args.Get(0).([]types.Container)
|
containers, ok := args.Get(0).([]types.Container)
|
||||||
if !ok && args.Get(0) != nil {
|
if !ok && args.Get(0) != nil {
|
||||||
@@ -33,7 +34,7 @@ func (m *mockedProxy) ContainerList(context.Context, container.ListOptions) ([]t
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockedProxy) ContainerLogs(ctx context.Context, id string, options container.LogsOptions) (io.ReadCloser, error) {
|
func (m *mockedProxy) ContainerLogs(ctx context.Context, id string, options docker.LogsOptions) (io.ReadCloser, error) {
|
||||||
args := m.Called(ctx, id, options)
|
args := m.Called(ctx, id, options)
|
||||||
reader, ok := args.Get(0).(io.ReadCloser)
|
reader, ok := args.Get(0).(io.ReadCloser)
|
||||||
if !ok && args.Get(0) != nil {
|
if !ok && args.Get(0) != nil {
|
||||||
@@ -47,11 +48,11 @@ func (m *mockedProxy) ContainerInspect(ctx context.Context, containerID string)
|
|||||||
return args.Get(0).(types.ContainerJSON), args.Error(1)
|
return args.Get(0).(types.ContainerJSON), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockedProxy) ContainerStats(ctx context.Context, containerID string, stream bool) (container.StatsResponseReader, error) {
|
func (m *mockedProxy) ContainerStats(ctx context.Context, containerID string, stream bool) (docker.StatsResponseReader, error) {
|
||||||
return container.StatsResponseReader{}, nil
|
return docker.StatsResponseReader{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockedProxy) ContainerStart(ctx context.Context, containerID string, options container.StartOptions) error {
|
func (m *mockedProxy) ContainerStart(ctx context.Context, containerID string, options docker.StartOptions) error {
|
||||||
|
|
||||||
args := m.Called(ctx, containerID, options)
|
args := m.Called(ctx, containerID, options)
|
||||||
err := args.Get(0)
|
err := args.Get(0)
|
||||||
@@ -63,7 +64,7 @@ func (m *mockedProxy) ContainerStart(ctx context.Context, containerID string, op
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockedProxy) ContainerStop(ctx context.Context, containerID string, options container.StopOptions) error {
|
func (m *mockedProxy) ContainerStop(ctx context.Context, containerID string, options docker.StopOptions) error {
|
||||||
args := m.Called(ctx, containerID, options)
|
args := m.Called(ctx, containerID, options)
|
||||||
err := args.Get(0)
|
err := args.Get(0)
|
||||||
|
|
||||||
@@ -74,7 +75,7 @@ func (m *mockedProxy) ContainerStop(ctx context.Context, containerID string, opt
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockedProxy) ContainerRestart(ctx context.Context, containerID string, options container.StopOptions) error {
|
func (m *mockedProxy) ContainerRestart(ctx context.Context, containerID string, options docker.StopOptions) error {
|
||||||
|
|
||||||
args := m.Called(ctx, containerID, options)
|
args := m.Called(ctx, containerID, options)
|
||||||
err := args.Get(0)
|
err := args.Get(0)
|
||||||
@@ -89,9 +90,9 @@ func (m *mockedProxy) ContainerRestart(ctx context.Context, containerID string,
|
|||||||
func Test_dockerClient_ListContainers_null(t *testing.T) {
|
func Test_dockerClient_ListContainers_null(t *testing.T) {
|
||||||
proxy := new(mockedProxy)
|
proxy := new(mockedProxy)
|
||||||
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(nil, nil)
|
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(nil, nil)
|
||||||
client := &httpClient{proxy, Host{ID: "localhost"}, system.Info{}}
|
client := &httpClient{proxy, container.Host{ID: "localhost"}, system.Info{}}
|
||||||
|
|
||||||
list, err := client.ListContainers(context.Background(), ContainerFilter{})
|
list, err := client.ListContainers(context.Background(), container.ContainerFilter{})
|
||||||
assert.Empty(t, list, "list should be empty")
|
assert.Empty(t, list, "list should be empty")
|
||||||
require.NoError(t, err, "error should not return an error.")
|
require.NoError(t, err, "error should not return an error.")
|
||||||
|
|
||||||
@@ -101,9 +102,9 @@ func Test_dockerClient_ListContainers_null(t *testing.T) {
|
|||||||
func Test_dockerClient_ListContainers_error(t *testing.T) {
|
func Test_dockerClient_ListContainers_error(t *testing.T) {
|
||||||
proxy := new(mockedProxy)
|
proxy := new(mockedProxy)
|
||||||
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(nil, errors.New("test"))
|
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(nil, errors.New("test"))
|
||||||
client := &httpClient{proxy, Host{ID: "localhost"}, system.Info{}}
|
client := &httpClient{proxy, container.Host{ID: "localhost"}, system.Info{}}
|
||||||
|
|
||||||
list, err := client.ListContainers(context.Background(), ContainerFilter{})
|
list, err := client.ListContainers(context.Background(), container.ContainerFilter{})
|
||||||
assert.Nil(t, list, "list should be nil")
|
assert.Nil(t, list, "list should be nil")
|
||||||
require.Error(t, err, "test.")
|
require.Error(t, err, "test.")
|
||||||
|
|
||||||
@@ -124,9 +125,9 @@ func Test_dockerClient_ListContainers_happy(t *testing.T) {
|
|||||||
|
|
||||||
proxy := new(mockedProxy)
|
proxy := new(mockedProxy)
|
||||||
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(containers, nil)
|
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(containers, nil)
|
||||||
client := &httpClient{proxy, Host{ID: "localhost"}, system.Info{}}
|
client := &httpClient{proxy, container.Host{ID: "localhost"}, system.Info{}}
|
||||||
|
|
||||||
list, err := client.ListContainers(context.Background(), ContainerFilter{})
|
list, err := client.ListContainers(context.Background(), container.ContainerFilter{})
|
||||||
require.NoError(t, err, "error should not return an error.")
|
require.NoError(t, err, "error should not return an error.")
|
||||||
|
|
||||||
Ids := []string{"1234567890_a", "abcdefghijkl"}
|
Ids := []string{"1234567890_a", "abcdefghijkl"}
|
||||||
@@ -149,7 +150,7 @@ func Test_dockerClient_ContainerLogs_happy(t *testing.T) {
|
|||||||
|
|
||||||
reader := io.NopCloser(bytes.NewReader(b))
|
reader := io.NopCloser(bytes.NewReader(b))
|
||||||
since := time.Date(2021, time.January, 1, 0, 0, 0, 0, time.UTC)
|
since := time.Date(2021, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||||
options := container.LogsOptions{
|
options := docker.LogsOptions{
|
||||||
ShowStdout: true,
|
ShowStdout: true,
|
||||||
ShowStderr: true,
|
ShowStderr: true,
|
||||||
Follow: true,
|
Follow: true,
|
||||||
@@ -158,8 +159,8 @@ func Test_dockerClient_ContainerLogs_happy(t *testing.T) {
|
|||||||
Since: "2020-12-31T23:59:59.95Z"}
|
Since: "2020-12-31T23:59:59.95Z"}
|
||||||
proxy.On("ContainerLogs", mock.Anything, id, options).Return(reader, nil)
|
proxy.On("ContainerLogs", mock.Anything, id, options).Return(reader, nil)
|
||||||
|
|
||||||
client := &httpClient{proxy, Host{ID: "localhost"}, system.Info{}}
|
client := &httpClient{proxy, container.Host{ID: "localhost"}, system.Info{}}
|
||||||
logReader, _ := client.ContainerLogs(context.Background(), id, since, STDALL)
|
logReader, _ := client.ContainerLogs(context.Background(), id, since, container.STDALL)
|
||||||
|
|
||||||
actual, _ := io.ReadAll(logReader)
|
actual, _ := io.ReadAll(logReader)
|
||||||
assert.Equal(t, string(b), string(actual), "message doesn't match expected")
|
assert.Equal(t, string(b), string(actual), "message doesn't match expected")
|
||||||
@@ -172,9 +173,9 @@ func Test_dockerClient_ContainerLogs_error(t *testing.T) {
|
|||||||
|
|
||||||
proxy.On("ContainerLogs", mock.Anything, id, mock.Anything).Return(nil, errors.New("test"))
|
proxy.On("ContainerLogs", mock.Anything, id, mock.Anything).Return(nil, errors.New("test"))
|
||||||
|
|
||||||
client := &httpClient{proxy, Host{ID: "localhost"}, system.Info{}}
|
client := &httpClient{proxy, container.Host{ID: "localhost"}, system.Info{}}
|
||||||
|
|
||||||
reader, err := client.ContainerLogs(context.Background(), id, time.Time{}, STDALL)
|
reader, err := client.ContainerLogs(context.Background(), id, time.Time{}, container.STDALL)
|
||||||
|
|
||||||
assert.Nil(t, reader, "reader should be nil")
|
assert.Nil(t, reader, "reader should be nil")
|
||||||
assert.Error(t, err, "error should have been returned")
|
assert.Error(t, err, "error should have been returned")
|
||||||
@@ -185,10 +186,10 @@ func Test_dockerClient_FindContainer_happy(t *testing.T) {
|
|||||||
proxy := new(mockedProxy)
|
proxy := new(mockedProxy)
|
||||||
|
|
||||||
state := &types.ContainerState{Status: "running", StartedAt: time.Now().Format(time.RFC3339Nano)}
|
state := &types.ContainerState{Status: "running", StartedAt: time.Now().Format(time.RFC3339Nano)}
|
||||||
json := types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{ID: "abcdefghijklmnopqrst", State: state}, Config: &container.Config{Tty: false}}
|
json := types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{ID: "abcdefghijklmnopqrst", State: state}, Config: &docker.Config{Tty: false}}
|
||||||
proxy.On("ContainerInspect", mock.Anything, "abcdefghijkl").Return(json, nil)
|
proxy.On("ContainerInspect", mock.Anything, "abcdefghijkl").Return(json, nil)
|
||||||
|
|
||||||
client := &httpClient{proxy, Host{ID: "localhost"}, system.Info{}}
|
client := &httpClient{proxy, container.Host{ID: "localhost"}, system.Info{}}
|
||||||
|
|
||||||
container, err := client.FindContainer(context.Background(), "abcdefghijkl")
|
container, err := client.FindContainer(context.Background(), "abcdefghijkl")
|
||||||
require.NoError(t, err, "error should not be thrown")
|
require.NoError(t, err, "error should not be thrown")
|
||||||
@@ -201,7 +202,7 @@ func Test_dockerClient_FindContainer_happy(t *testing.T) {
|
|||||||
func Test_dockerClient_FindContainer_error(t *testing.T) {
|
func Test_dockerClient_FindContainer_error(t *testing.T) {
|
||||||
proxy := new(mockedProxy)
|
proxy := new(mockedProxy)
|
||||||
proxy.On("ContainerInspect", mock.Anything, "not_valid").Return(types.ContainerJSON{}, errors.New("not found"))
|
proxy.On("ContainerInspect", mock.Anything, "not_valid").Return(types.ContainerJSON{}, errors.New("not found"))
|
||||||
client := &httpClient{proxy, Host{ID: "localhost"}, system.Info{}}
|
client := &httpClient{proxy, container.Host{ID: "localhost"}, system.Info{}}
|
||||||
|
|
||||||
_, err := client.FindContainer(context.Background(), "not_valid")
|
_, err := client.FindContainer(context.Background(), "not_valid")
|
||||||
require.Error(t, err, "error should be thrown")
|
require.Error(t, err, "error should be thrown")
|
||||||
@@ -211,24 +212,24 @@ func Test_dockerClient_FindContainer_error(t *testing.T) {
|
|||||||
|
|
||||||
func Test_dockerClient_ContainerActions_happy(t *testing.T) {
|
func Test_dockerClient_ContainerActions_happy(t *testing.T) {
|
||||||
proxy := new(mockedProxy)
|
proxy := new(mockedProxy)
|
||||||
client := &httpClient{proxy, Host{ID: "localhost"}, system.Info{}}
|
client := &httpClient{proxy, container.Host{ID: "localhost"}, system.Info{}}
|
||||||
|
|
||||||
state := &types.ContainerState{Status: "running", StartedAt: time.Now().Format(time.RFC3339Nano)}
|
state := &types.ContainerState{Status: "running", StartedAt: time.Now().Format(time.RFC3339Nano)}
|
||||||
json := types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{ID: "abcdefghijkl", State: state}, Config: &container.Config{Tty: false}}
|
json := types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{ID: "abcdefghijkl", State: state}, Config: &docker.Config{Tty: false}}
|
||||||
|
|
||||||
proxy.On("ContainerInspect", mock.Anything, "abcdefghijkl").Return(json, nil)
|
proxy.On("ContainerInspect", mock.Anything, "abcdefghijkl").Return(json, nil)
|
||||||
proxy.On("ContainerStart", mock.Anything, "abcdefghijkl", mock.Anything).Return(nil)
|
proxy.On("ContainerStart", mock.Anything, "abcdefghijkl", mock.Anything).Return(nil)
|
||||||
proxy.On("ContainerStop", mock.Anything, "abcdefghijkl", mock.Anything).Return(nil)
|
proxy.On("ContainerStop", mock.Anything, "abcdefghijkl", mock.Anything).Return(nil)
|
||||||
proxy.On("ContainerRestart", mock.Anything, "abcdefghijkl", mock.Anything).Return(nil)
|
proxy.On("ContainerRestart", mock.Anything, "abcdefghijkl", mock.Anything).Return(nil)
|
||||||
|
|
||||||
container, err := client.FindContainer(context.Background(), "abcdefghijkl")
|
c, err := client.FindContainer(context.Background(), "abcdefghijkl")
|
||||||
require.NoError(t, err, "error should not be thrown")
|
require.NoError(t, err, "error should not be thrown")
|
||||||
|
|
||||||
assert.Equal(t, container.ID, "abcdefghijkl")
|
assert.Equal(t, c.ID, "abcdefghijkl")
|
||||||
|
|
||||||
actions := []string{"start", "stop", "restart"}
|
actions := []string{"start", "stop", "restart"}
|
||||||
for _, action := range actions {
|
for _, action := range actions {
|
||||||
err := client.ContainerActions(context.Background(), ContainerAction(action), container.ID)
|
err := client.ContainerActions(context.Background(), container.ContainerAction(action), c.ID)
|
||||||
require.NoError(t, err, "error should not be thrown")
|
require.NoError(t, err, "error should not be thrown")
|
||||||
assert.Equal(t, err, nil)
|
assert.Equal(t, err, nil)
|
||||||
}
|
}
|
||||||
@@ -239,18 +240,18 @@ func Test_dockerClient_ContainerActions_happy(t *testing.T) {
|
|||||||
func Test_dockerClient_ContainerActions_error(t *testing.T) {
|
func Test_dockerClient_ContainerActions_error(t *testing.T) {
|
||||||
|
|
||||||
proxy := new(mockedProxy)
|
proxy := new(mockedProxy)
|
||||||
client := &httpClient{proxy, Host{ID: "localhost"}, system.Info{}}
|
client := &httpClient{proxy, container.Host{ID: "localhost"}, system.Info{}}
|
||||||
proxy.On("ContainerInspect", mock.Anything, "random-id").Return(types.ContainerJSON{}, errors.New("not found"))
|
proxy.On("ContainerInspect", mock.Anything, "random-id").Return(types.ContainerJSON{}, errors.New("not found"))
|
||||||
proxy.On("ContainerStart", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("test"))
|
proxy.On("ContainerStart", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("test"))
|
||||||
proxy.On("ContainerStop", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("test"))
|
proxy.On("ContainerStop", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("test"))
|
||||||
proxy.On("ContainerRestart", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("test"))
|
proxy.On("ContainerRestart", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("test"))
|
||||||
|
|
||||||
container, err := client.FindContainer(context.Background(), "random-id")
|
c, err := client.FindContainer(context.Background(), "random-id")
|
||||||
require.Error(t, err, "error should be thrown")
|
require.Error(t, err, "error should be thrown")
|
||||||
|
|
||||||
actions := []string{"start", "stop", "restart"}
|
actions := []string{"start", "stop", "restart"}
|
||||||
for _, action := range actions {
|
for _, action := range actions {
|
||||||
err := client.ContainerActions(context.Background(), ContainerAction(action), container.ID)
|
err := client.ContainerActions(context.Background(), container.ContainerAction(action), c.ID)
|
||||||
require.Error(t, err, "error should be thrown")
|
require.Error(t, err, "error should be thrown")
|
||||||
assert.Error(t, err, "error should have been returned")
|
assert.Error(t, err, "error should have been returned")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/agent"
|
"github.com/amir20/dozzle/internal/agent"
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ func RPCRequest(ctx context.Context, addr string, certs tls.Certificate) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("Failed to create agent client")
|
log.Fatal().Err(err).Msg("Failed to create agent client")
|
||||||
}
|
}
|
||||||
containers, err := client.ListContainers(ctx, docker.ContainerFilter{})
|
containers, err := client.ListContainers(ctx, container.ContainerFilter{})
|
||||||
log.Trace().Int("containers", len(containers)).Msg("Healtcheck RPC request completed")
|
log.Trace().Int("containers", len(containers)).Msg("Healtcheck RPC request completed")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ package cli
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/amir20/dozzle/internal/analytics"
|
"github.com/amir20/dozzle/internal/analytics"
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/amir20/dozzle/types"
|
"github.com/amir20/dozzle/types"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StartEvent(args Args, mode string, client docker.Client, subCommand string) {
|
func StartEvent(args Args, mode string, client container.Client, subCommand string) {
|
||||||
if args.NoAnalytics {
|
if args.NoAnalytics {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ func StartEvent(args Args, mode string, client docker.Client, subCommand string)
|
|||||||
host := client.Host()
|
host := client.Host()
|
||||||
event.ServerID = host.ID
|
event.ServerID = host.ID
|
||||||
event.ServerVersion = host.DockerVersion
|
event.ServerVersion = host.DockerVersion
|
||||||
event.IsSwarmMode = client.SystemInfo().Swarm.NodeID != ""
|
event.IsSwarmMode = host.Swarm
|
||||||
} else {
|
} else {
|
||||||
event.ServerID = "n/a"
|
event.ServerID = "n/a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,19 +4,20 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"embed"
|
"embed"
|
||||||
|
|
||||||
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/docker"
|
||||||
docker_support "github.com/amir20/dozzle/internal/support/docker"
|
docker_support "github.com/amir20/dozzle/internal/support/docker"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateMultiHostService(embeddedCerts embed.FS, args Args) (docker.Client, *docker_support.MultiHostService) {
|
func CreateMultiHostService(embeddedCerts embed.FS, args Args) (container.Client, *docker_support.MultiHostService) {
|
||||||
var clients []docker_support.ClientService
|
var clients []docker_support.ClientService
|
||||||
if len(args.RemoteHost) > 0 {
|
if len(args.RemoteHost) > 0 {
|
||||||
log.Info().Msg(`Consider using Dozzle's remote agent to manage remote hosts. See https://dozzle.dev/guide/agent for more information`)
|
log.Info().Msg(`Consider using Dozzle's remote agent to manage remote hosts. See https://dozzle.dev/guide/agent for more information`)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, remoteHost := range args.RemoteHost {
|
for _, remoteHost := range args.RemoteHost {
|
||||||
host, err := docker.ParseConnection(remoteHost)
|
host, err := container.ParseConnection(remoteHost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Interface("host", remoteHost).Msg("Could not parse remote host")
|
log.Fatal().Err(err).Interface("host", remoteHost).Msg("Could not parse remote host")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/agent"
|
"github.com/amir20/dozzle/internal/agent"
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type agentService struct {
|
type agentService struct {
|
||||||
client *agent.Client
|
client *agent.Client
|
||||||
host docker.Host
|
host container.Host
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAgentService(client *agent.Client) ClientService {
|
func NewAgentService(client *agent.Client) ClientService {
|
||||||
@@ -22,28 +22,28 @@ func NewAgentService(client *agent.Client) ClientService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *agentService) FindContainer(ctx context.Context, id string, filter docker.ContainerFilter) (docker.Container, error) {
|
func (a *agentService) FindContainer(ctx context.Context, id string, filter container.ContainerFilter) (container.Container, error) {
|
||||||
return a.client.FindContainer(ctx, id)
|
return a.client.FindContainer(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *agentService) RawLogs(ctx context.Context, container docker.Container, from time.Time, to time.Time, stdTypes docker.StdType) (io.ReadCloser, error) {
|
func (a *agentService) RawLogs(ctx context.Context, container container.Container, from time.Time, to time.Time, stdTypes container.StdType) (io.ReadCloser, error) {
|
||||||
return a.client.StreamRawBytes(ctx, container.ID, from, to, stdTypes)
|
return a.client.StreamRawBytes(ctx, container.ID, from, to, stdTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *agentService) LogsBetweenDates(ctx context.Context, container docker.Container, from time.Time, to time.Time, stdTypes docker.StdType) (<-chan *docker.LogEvent, error) {
|
func (a *agentService) LogsBetweenDates(ctx context.Context, container container.Container, from time.Time, to time.Time, stdTypes container.StdType) (<-chan *container.LogEvent, error) {
|
||||||
return a.client.LogsBetweenDates(ctx, container.ID, from, to, stdTypes)
|
return a.client.LogsBetweenDates(ctx, container.ID, from, to, stdTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *agentService) StreamLogs(ctx context.Context, container docker.Container, from time.Time, stdTypes docker.StdType, events chan<- *docker.LogEvent) error {
|
func (a *agentService) StreamLogs(ctx context.Context, container container.Container, from time.Time, stdTypes container.StdType, events chan<- *container.LogEvent) error {
|
||||||
return a.client.StreamContainerLogs(ctx, container.ID, from, stdTypes, events)
|
return a.client.StreamContainerLogs(ctx, container.ID, from, stdTypes, events)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *agentService) ListContainers(ctx context.Context, filter docker.ContainerFilter) ([]docker.Container, error) {
|
func (a *agentService) ListContainers(ctx context.Context, filter container.ContainerFilter) ([]container.Container, error) {
|
||||||
log.Debug().Interface("filter", filter).Msg("Listing containers from agent")
|
log.Debug().Interface("filter", filter).Msg("Listing containers from agent")
|
||||||
return a.client.ListContainers(ctx, filter)
|
return a.client.ListContainers(ctx, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *agentService) Host(ctx context.Context) (docker.Host, error) {
|
func (a *agentService) Host(ctx context.Context) (container.Host, error) {
|
||||||
host, err := a.client.Host(ctx)
|
host, err := a.client.Host(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
host := a.host
|
host := a.host
|
||||||
@@ -55,18 +55,18 @@ func (a *agentService) Host(ctx context.Context) (docker.Host, error) {
|
|||||||
return a.host, err
|
return a.host, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *agentService) SubscribeStats(ctx context.Context, stats chan<- docker.ContainerStat) {
|
func (a *agentService) SubscribeStats(ctx context.Context, stats chan<- container.ContainerStat) {
|
||||||
go a.client.StreamStats(ctx, stats)
|
go a.client.StreamStats(ctx, stats)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *agentService) SubscribeEvents(ctx context.Context, events chan<- docker.ContainerEvent) {
|
func (a *agentService) SubscribeEvents(ctx context.Context, events chan<- container.ContainerEvent) {
|
||||||
go a.client.StreamEvents(ctx, events)
|
go a.client.StreamEvents(ctx, events)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *agentService) SubscribeContainersStarted(ctx context.Context, containers chan<- docker.Container) {
|
func (d *agentService) SubscribeContainersStarted(ctx context.Context, containers chan<- container.Container) {
|
||||||
go d.client.StreamNewContainers(ctx, containers)
|
go d.client.StreamNewContainers(ctx, containers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *agentService) ContainerAction(ctx context.Context, container docker.Container, action docker.ContainerAction) error {
|
func (a *agentService) ContainerAction(ctx context.Context, container container.Container, action container.ContainerAction) error {
|
||||||
return a.client.ContainerAction(ctx, container.ID, action)
|
return a.client.ContainerAction(ctx, container.ID, action)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,59 +5,59 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ClientService interface {
|
type ClientService interface {
|
||||||
FindContainer(ctx context.Context, id string, filter docker.ContainerFilter) (docker.Container, error)
|
FindContainer(ctx context.Context, id string, filter container.ContainerFilter) (container.Container, error)
|
||||||
ListContainers(ctx context.Context, filter docker.ContainerFilter) ([]docker.Container, error)
|
ListContainers(ctx context.Context, filter container.ContainerFilter) ([]container.Container, error)
|
||||||
Host(ctx context.Context) (docker.Host, error)
|
Host(ctx context.Context) (container.Host, error)
|
||||||
ContainerAction(ctx context.Context, container docker.Container, action docker.ContainerAction) error
|
ContainerAction(ctx context.Context, container container.Container, action container.ContainerAction) error
|
||||||
LogsBetweenDates(ctx context.Context, container docker.Container, from time.Time, to time.Time, stdTypes docker.StdType) (<-chan *docker.LogEvent, error)
|
LogsBetweenDates(ctx context.Context, container container.Container, from time.Time, to time.Time, stdTypes container.StdType) (<-chan *container.LogEvent, error)
|
||||||
RawLogs(ctx context.Context, container docker.Container, from time.Time, to time.Time, stdTypes docker.StdType) (io.ReadCloser, error)
|
RawLogs(ctx context.Context, container container.Container, from time.Time, to time.Time, stdTypes container.StdType) (io.ReadCloser, error)
|
||||||
|
|
||||||
// Subscriptions
|
// Subscriptions
|
||||||
SubscribeStats(ctx context.Context, stats chan<- docker.ContainerStat)
|
SubscribeStats(ctx context.Context, stats chan<- container.ContainerStat)
|
||||||
SubscribeEvents(ctx context.Context, events chan<- docker.ContainerEvent)
|
SubscribeEvents(ctx context.Context, events chan<- container.ContainerEvent)
|
||||||
SubscribeContainersStarted(ctx context.Context, containers chan<- docker.Container)
|
SubscribeContainersStarted(ctx context.Context, containers chan<- container.Container)
|
||||||
|
|
||||||
// Blocking streaming functions that should be used in a goroutine
|
// Blocking streaming functions that should be used in a goroutine
|
||||||
StreamLogs(ctx context.Context, container docker.Container, from time.Time, stdTypes docker.StdType, events chan<- *docker.LogEvent) error
|
StreamLogs(ctx context.Context, container container.Container, from time.Time, stdTypes container.StdType, events chan<- *container.LogEvent) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type dockerClientService struct {
|
type dockerClientService struct {
|
||||||
client docker.Client
|
client container.Client
|
||||||
store *docker.ContainerStore
|
store *container.ContainerStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDockerClientService(client docker.Client, filter docker.ContainerFilter) ClientService {
|
func NewDockerClientService(client container.Client, filter container.ContainerFilter) ClientService {
|
||||||
return &dockerClientService{
|
return &dockerClientService{
|
||||||
client: client,
|
client: client,
|
||||||
store: docker.NewContainerStore(context.Background(), client, filter),
|
store: container.NewContainerStore(context.Background(), client, filter),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dockerClientService) RawLogs(ctx context.Context, container docker.Container, from time.Time, to time.Time, stdTypes docker.StdType) (io.ReadCloser, error) {
|
func (d *dockerClientService) RawLogs(ctx context.Context, container container.Container, from time.Time, to time.Time, stdTypes container.StdType) (io.ReadCloser, error) {
|
||||||
return d.client.ContainerLogsBetweenDates(ctx, container.ID, from, to, stdTypes)
|
return d.client.ContainerLogsBetweenDates(ctx, container.ID, from, to, stdTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dockerClientService) LogsBetweenDates(ctx context.Context, container docker.Container, from time.Time, to time.Time, stdTypes docker.StdType) (<-chan *docker.LogEvent, error) {
|
func (d *dockerClientService) LogsBetweenDates(ctx context.Context, c container.Container, from time.Time, to time.Time, stdTypes container.StdType) (<-chan *container.LogEvent, error) {
|
||||||
reader, err := d.client.ContainerLogsBetweenDates(ctx, container.ID, from, to, stdTypes)
|
reader, err := d.client.ContainerLogsBetweenDates(ctx, c.ID, from, to, stdTypes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
g := docker.NewEventGenerator(ctx, reader, container)
|
g := container.NewEventGenerator(ctx, reader, c)
|
||||||
return g.Events, nil
|
return g.Events, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dockerClientService) StreamLogs(ctx context.Context, container docker.Container, from time.Time, stdTypes docker.StdType, events chan<- *docker.LogEvent) error {
|
func (d *dockerClientService) StreamLogs(ctx context.Context, c container.Container, from time.Time, stdTypes container.StdType, events chan<- *container.LogEvent) error {
|
||||||
reader, err := d.client.ContainerLogs(ctx, container.ID, from, stdTypes)
|
reader, err := d.client.ContainerLogs(ctx, c.ID, from, stdTypes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
g := docker.NewEventGenerator(ctx, reader, container)
|
g := container.NewEventGenerator(ctx, reader, c)
|
||||||
for event := range g.Events {
|
for event := range g.Events {
|
||||||
events <- event
|
events <- event
|
||||||
}
|
}
|
||||||
@@ -70,30 +70,30 @@ func (d *dockerClientService) StreamLogs(ctx context.Context, container docker.C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dockerClientService) FindContainer(ctx context.Context, id string, filter docker.ContainerFilter) (docker.Container, error) {
|
func (d *dockerClientService) FindContainer(ctx context.Context, id string, filter container.ContainerFilter) (container.Container, error) {
|
||||||
return d.store.FindContainer(id, filter)
|
return d.store.FindContainer(id, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dockerClientService) ContainerAction(ctx context.Context, container docker.Container, action docker.ContainerAction) error {
|
func (d *dockerClientService) ContainerAction(ctx context.Context, container container.Container, action container.ContainerAction) error {
|
||||||
return d.client.ContainerActions(ctx, action, container.ID)
|
return d.client.ContainerActions(ctx, action, container.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dockerClientService) ListContainers(ctx context.Context, filter docker.ContainerFilter) ([]docker.Container, error) {
|
func (d *dockerClientService) ListContainers(ctx context.Context, filter container.ContainerFilter) ([]container.Container, error) {
|
||||||
return d.store.ListContainers(filter)
|
return d.store.ListContainers(filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dockerClientService) Host(ctx context.Context) (docker.Host, error) {
|
func (d *dockerClientService) Host(ctx context.Context) (container.Host, error) {
|
||||||
return d.client.Host(), nil
|
return d.client.Host(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dockerClientService) SubscribeStats(ctx context.Context, stats chan<- docker.ContainerStat) {
|
func (d *dockerClientService) SubscribeStats(ctx context.Context, stats chan<- container.ContainerStat) {
|
||||||
d.store.SubscribeStats(ctx, stats)
|
d.store.SubscribeStats(ctx, stats)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dockerClientService) SubscribeEvents(ctx context.Context, events chan<- docker.ContainerEvent) {
|
func (d *dockerClientService) SubscribeEvents(ctx context.Context, events chan<- container.ContainerEvent) {
|
||||||
d.store.SubscribeEvents(ctx, events)
|
d.store.SubscribeEvents(ctx, events)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dockerClientService) SubscribeContainersStarted(ctx context.Context, containers chan<- docker.Container) {
|
func (d *dockerClientService) SubscribeContainersStarted(ctx context.Context, containers chan<- container.Container) {
|
||||||
d.store.SubscribeNewContainers(ctx, containers)
|
d.store.SubscribeNewContainers(ctx, containers)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,26 +5,26 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
)
|
)
|
||||||
|
|
||||||
type containerService struct {
|
type containerService struct {
|
||||||
clientService ClientService
|
clientService ClientService
|
||||||
Container docker.Container
|
Container container.Container
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerService) RawLogs(ctx context.Context, from time.Time, to time.Time, stdTypes docker.StdType) (io.ReadCloser, error) {
|
func (c *containerService) RawLogs(ctx context.Context, from time.Time, to time.Time, stdTypes container.StdType) (io.ReadCloser, error) {
|
||||||
return c.clientService.RawLogs(ctx, c.Container, from, to, stdTypes)
|
return c.clientService.RawLogs(ctx, c.Container, from, to, stdTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerService) LogsBetweenDates(ctx context.Context, from time.Time, to time.Time, stdTypes docker.StdType) (<-chan *docker.LogEvent, error) {
|
func (c *containerService) LogsBetweenDates(ctx context.Context, from time.Time, to time.Time, stdTypes container.StdType) (<-chan *container.LogEvent, error) {
|
||||||
return c.clientService.LogsBetweenDates(ctx, c.Container, from, to, stdTypes)
|
return c.clientService.LogsBetweenDates(ctx, c.Container, from, to, stdTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerService) StreamLogs(ctx context.Context, from time.Time, stdTypes docker.StdType, events chan<- *docker.LogEvent) error {
|
func (c *containerService) StreamLogs(ctx context.Context, from time.Time, stdTypes container.StdType, events chan<- *container.LogEvent) error {
|
||||||
return c.clientService.StreamLogs(ctx, c.Container, from, stdTypes, events)
|
return c.clientService.StreamLogs(ctx, c.Container, from, stdTypes, events)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerService) Action(ctx context.Context, action docker.ContainerAction) error {
|
func (c *containerService) Action(ctx context.Context, action container.ContainerAction) error {
|
||||||
return c.clientService.ContainerAction(ctx, c.Container, action)
|
return c.clientService.ContainerAction(ctx, c.Container, action)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ContainerFilter = func(*docker.Container) bool
|
type ContainerFilter = func(*container.Container) bool
|
||||||
|
|
||||||
type HostUnavailableError struct {
|
type HostUnavailableError struct {
|
||||||
Host docker.Host
|
Host container.Host
|
||||||
Err error
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,9 +24,9 @@ type ClientManager interface {
|
|||||||
Find(id string) (ClientService, bool)
|
Find(id string) (ClientService, bool)
|
||||||
List() []ClientService
|
List() []ClientService
|
||||||
RetryAndList() ([]ClientService, []error)
|
RetryAndList() ([]ClientService, []error)
|
||||||
Subscribe(ctx context.Context, channel chan<- docker.Host)
|
Subscribe(ctx context.Context, channel chan<- container.Host)
|
||||||
Hosts(ctx context.Context) []docker.Host
|
Hosts(ctx context.Context) []container.Host
|
||||||
LocalClients() []docker.Client
|
LocalClients() []container.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
type MultiHostService struct {
|
type MultiHostService struct {
|
||||||
@@ -43,7 +43,7 @@ func NewMultiHostService(manager ClientManager, timeout time.Duration) *MultiHos
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MultiHostService) FindContainer(host string, id string, filter docker.ContainerFilter) (*containerService, error) {
|
func (m *MultiHostService) FindContainer(host string, id string, filter container.ContainerFilter) (*containerService, error) {
|
||||||
client, ok := m.manager.Find(host)
|
client, ok := m.manager.Find(host)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("host %s not found", host)
|
return nil, fmt.Errorf("host %s not found", host)
|
||||||
@@ -61,7 +61,7 @@ func (m *MultiHostService) FindContainer(host string, id string, filter docker.C
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MultiHostService) ListContainersForHost(host string, filter docker.ContainerFilter) ([]docker.Container, error) {
|
func (m *MultiHostService) ListContainersForHost(host string, filter container.ContainerFilter) ([]container.Container, error) {
|
||||||
client, ok := m.manager.Find(host)
|
client, ok := m.manager.Find(host)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("host %s not found", host)
|
return nil, fmt.Errorf("host %s not found", host)
|
||||||
@@ -72,8 +72,8 @@ func (m *MultiHostService) ListContainersForHost(host string, filter docker.Cont
|
|||||||
return client.ListContainers(ctx, filter)
|
return client.ListContainers(ctx, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MultiHostService) ListAllContainers(filter docker.ContainerFilter) ([]docker.Container, []error) {
|
func (m *MultiHostService) ListAllContainers(filter container.ContainerFilter) ([]container.Container, []error) {
|
||||||
containers := make([]docker.Container, 0)
|
containers := make([]container.Container, 0)
|
||||||
clients, errors := m.manager.RetryAndList()
|
clients, errors := m.manager.RetryAndList()
|
||||||
|
|
||||||
for _, client := range clients {
|
for _, client := range clients {
|
||||||
@@ -94,9 +94,9 @@ func (m *MultiHostService) ListAllContainers(filter docker.ContainerFilter) ([]d
|
|||||||
return containers, errors
|
return containers, errors
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MultiHostService) ListAllContainersFiltered(userFilter docker.ContainerFilter, filter ContainerFilter) ([]docker.Container, []error) {
|
func (m *MultiHostService) ListAllContainersFiltered(userFilter container.ContainerFilter, filter ContainerFilter) ([]container.Container, []error) {
|
||||||
containers, err := m.ListAllContainers(userFilter)
|
containers, err := m.ListAllContainers(userFilter)
|
||||||
filtered := make([]docker.Container, 0, len(containers))
|
filtered := make([]container.Container, 0, len(containers))
|
||||||
for _, container := range containers {
|
for _, container := range containers {
|
||||||
if filter(&container) {
|
if filter(&container) {
|
||||||
filtered = append(filtered, container)
|
filtered = append(filtered, container)
|
||||||
@@ -105,15 +105,15 @@ func (m *MultiHostService) ListAllContainersFiltered(userFilter docker.Container
|
|||||||
return filtered, err
|
return filtered, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MultiHostService) SubscribeEventsAndStats(ctx context.Context, events chan<- docker.ContainerEvent, stats chan<- docker.ContainerStat) {
|
func (m *MultiHostService) SubscribeEventsAndStats(ctx context.Context, events chan<- container.ContainerEvent, stats chan<- container.ContainerStat) {
|
||||||
for _, client := range m.manager.List() {
|
for _, client := range m.manager.List() {
|
||||||
client.SubscribeEvents(ctx, events)
|
client.SubscribeEvents(ctx, events)
|
||||||
client.SubscribeStats(ctx, stats)
|
client.SubscribeStats(ctx, stats)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MultiHostService) SubscribeContainersStarted(ctx context.Context, containers chan<- docker.Container, filter ContainerFilter) {
|
func (m *MultiHostService) SubscribeContainersStarted(ctx context.Context, containers chan<- container.Container, filter ContainerFilter) {
|
||||||
newContainers := make(chan docker.Container)
|
newContainers := make(chan container.Container)
|
||||||
for _, client := range m.manager.List() {
|
for _, client := range m.manager.List() {
|
||||||
client.SubscribeContainersStarted(ctx, newContainers)
|
client.SubscribeContainersStarted(ctx, newContainers)
|
||||||
}
|
}
|
||||||
@@ -139,25 +139,25 @@ func (m *MultiHostService) TotalClients() int {
|
|||||||
return len(m.manager.List())
|
return len(m.manager.List())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MultiHostService) Hosts() []docker.Host {
|
func (m *MultiHostService) Hosts() []container.Host {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), m.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), m.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
return m.manager.Hosts(ctx)
|
return m.manager.Hosts(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MultiHostService) LocalHost() (docker.Host, error) {
|
func (m *MultiHostService) LocalHost() (container.Host, error) {
|
||||||
for _, host := range m.Hosts() {
|
for _, host := range m.Hosts() {
|
||||||
if host.Type == "local" {
|
if host.Type == "local" {
|
||||||
return host, nil
|
return host, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return docker.Host{}, fmt.Errorf("local host not found")
|
return container.Host{}, fmt.Errorf("local host not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MultiHostService) SubscribeAvailableHosts(ctx context.Context, hosts chan<- docker.Host) {
|
func (m *MultiHostService) SubscribeAvailableHosts(ctx context.Context, hosts chan<- container.Host) {
|
||||||
m.manager.Subscribe(ctx, hosts)
|
m.manager.Subscribe(ctx, hosts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MultiHostService) LocalClients() []docker.Client {
|
func (m *MultiHostService) LocalClients() []container.Client {
|
||||||
return m.manager.LocalClients()
|
return m.manager.LocalClients()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/agent"
|
"github.com/amir20/dozzle/internal/agent"
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/puzpuzpuz/xsync/v3"
|
"github.com/puzpuzpuz/xsync/v3"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
lop "github.com/samber/lo/parallel"
|
lop "github.com/samber/lo/parallel"
|
||||||
@@ -21,7 +21,7 @@ type RetriableClientManager struct {
|
|||||||
failedAgents []string
|
failedAgents []string
|
||||||
certs tls.Certificate
|
certs tls.Certificate
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
subscribers *xsync.MapOf[context.Context, chan<- docker.Host]
|
subscribers *xsync.MapOf[context.Context, chan<- container.Host]
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,12 +72,12 @@ func NewRetriableClientManager(agents []string, timeout time.Duration, certs tls
|
|||||||
clients: clientMap,
|
clients: clientMap,
|
||||||
failedAgents: failed,
|
failedAgents: failed,
|
||||||
certs: certs,
|
certs: certs,
|
||||||
subscribers: xsync.NewMapOf[context.Context, chan<- docker.Host](),
|
subscribers: xsync.NewMapOf[context.Context, chan<- container.Host](),
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *RetriableClientManager) Subscribe(ctx context.Context, channel chan<- docker.Host) {
|
func (m *RetriableClientManager) Subscribe(ctx context.Context, channel chan<- container.Host) {
|
||||||
m.subscribers.Store(ctx, channel)
|
m.subscribers.Store(ctx, channel)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@@ -111,11 +111,11 @@ func (m *RetriableClientManager) RetryAndList() ([]ClientService, []error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m.clients[host.ID] = NewAgentService(agent)
|
m.clients[host.ID] = NewAgentService(agent)
|
||||||
m.subscribers.Range(func(ctx context.Context, channel chan<- docker.Host) bool {
|
m.subscribers.Range(func(ctx context.Context, channel chan<- container.Host) bool {
|
||||||
host.Available = true
|
host.Available = true
|
||||||
|
|
||||||
// We don't want to block the subscribers in event.go
|
// We don't want to block the subscribers in event.go
|
||||||
go func(host docker.Host) {
|
go func(host container.Host) {
|
||||||
select {
|
select {
|
||||||
case channel <- host:
|
case channel <- host:
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
@@ -152,10 +152,10 @@ func (m *RetriableClientManager) String() string {
|
|||||||
return fmt.Sprintf("RetriableClientManager{clients: %d, failedAgents: %d}", len(m.clients), len(m.failedAgents))
|
return fmt.Sprintf("RetriableClientManager{clients: %d, failedAgents: %d}", len(m.clients), len(m.failedAgents))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *RetriableClientManager) Hosts(ctx context.Context) []docker.Host {
|
func (m *RetriableClientManager) Hosts(ctx context.Context) []container.Host {
|
||||||
clients := m.List()
|
clients := m.List()
|
||||||
|
|
||||||
hosts := lop.Map(clients, func(client ClientService, _ int) docker.Host {
|
hosts := lop.Map(clients, func(client ClientService, _ int) container.Host {
|
||||||
host, err := client.Host(ctx)
|
host, err := client.Host(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn().Err(err).Str("host", host.Name).Msg("error fetching host info for client")
|
log.Warn().Err(err).Str("host", host.Name).Msg("error fetching host info for client")
|
||||||
@@ -168,7 +168,7 @@ func (m *RetriableClientManager) Hosts(ctx context.Context) []docker.Host {
|
|||||||
})
|
})
|
||||||
|
|
||||||
for _, endpoint := range m.failedAgents {
|
for _, endpoint := range m.failedAgents {
|
||||||
hosts = append(hosts, docker.Host{
|
hosts = append(hosts, container.Host{
|
||||||
ID: endpoint,
|
ID: endpoint,
|
||||||
Name: endpoint,
|
Name: endpoint,
|
||||||
Endpoint: endpoint,
|
Endpoint: endpoint,
|
||||||
@@ -180,10 +180,10 @@ func (m *RetriableClientManager) Hosts(ctx context.Context) []docker.Host {
|
|||||||
return hosts
|
return hosts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *RetriableClientManager) LocalClients() []docker.Client {
|
func (m *RetriableClientManager) LocalClients() []container.Client {
|
||||||
services := m.List()
|
services := m.List()
|
||||||
|
|
||||||
clients := make([]docker.Client, 0)
|
clients := make([]container.Client, 0)
|
||||||
|
|
||||||
for _, service := range services {
|
for _, service := range services {
|
||||||
if clientService, ok := service.(*dockerClientService); ok {
|
if clientService, ok := service.(*dockerClientService); ok {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/agent"
|
"github.com/amir20/dozzle/internal/agent"
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/puzpuzpuz/xsync/v3"
|
"github.com/puzpuzpuz/xsync/v3"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
lop "github.com/samber/lo/parallel"
|
lop "github.com/samber/lo/parallel"
|
||||||
@@ -22,8 +22,8 @@ type SwarmClientManager struct {
|
|||||||
clients map[string]ClientService
|
clients map[string]ClientService
|
||||||
certs tls.Certificate
|
certs tls.Certificate
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
subscribers *xsync.MapOf[context.Context, chan<- docker.Host]
|
subscribers *xsync.MapOf[context.Context, chan<- container.Host]
|
||||||
localClient docker.Client
|
localClient container.Client
|
||||||
localIPs []string
|
localIPs []string
|
||||||
name string
|
name string
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
@@ -47,7 +47,7 @@ func localIPs() []string {
|
|||||||
return ips
|
return ips
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSwarmClientManager(localClient docker.Client, certs tls.Certificate, timeout time.Duration, agentManager *RetriableClientManager, filter docker.ContainerFilter) *SwarmClientManager {
|
func NewSwarmClientManager(localClient container.Client, certs tls.Certificate, timeout time.Duration, agentManager *RetriableClientManager, filter container.ContainerFilter) *SwarmClientManager {
|
||||||
clientMap := make(map[string]ClientService)
|
clientMap := make(map[string]ClientService)
|
||||||
localService := NewDockerClientService(localClient, filter)
|
localService := NewDockerClientService(localClient, filter)
|
||||||
clientMap[localClient.Host().ID] = localService
|
clientMap[localClient.Host().ID] = localService
|
||||||
@@ -59,12 +59,12 @@ func NewSwarmClientManager(localClient docker.Client, certs tls.Certificate, tim
|
|||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
container, err := localClient.FindContainer(ctx, id)
|
c, err := localClient.FindContainer(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("error finding own container when looking for swarm service name")
|
log.Fatal().Err(err).Msg("error finding own container when looking for swarm service name")
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceName := container.Labels["com.docker.swarm.service.name"]
|
serviceName := c.Labels["com.docker.swarm.service.name"]
|
||||||
|
|
||||||
log.Debug().Str("service", serviceName).Msg("found swarm service name")
|
log.Debug().Str("service", serviceName).Msg("found swarm service name")
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ func NewSwarmClientManager(localClient docker.Client, certs tls.Certificate, tim
|
|||||||
localClient: localClient,
|
localClient: localClient,
|
||||||
clients: clientMap,
|
clients: clientMap,
|
||||||
certs: certs,
|
certs: certs,
|
||||||
subscribers: xsync.NewMapOf[context.Context, chan<- docker.Host](),
|
subscribers: xsync.NewMapOf[context.Context, chan<- container.Host](),
|
||||||
localIPs: localIPs(),
|
localIPs: localIPs(),
|
||||||
name: serviceName,
|
name: serviceName,
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
@@ -80,7 +80,7 @@ func NewSwarmClientManager(localClient docker.Client, certs tls.Certificate, tim
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *SwarmClientManager) Subscribe(ctx context.Context, channel chan<- docker.Host) {
|
func (m *SwarmClientManager) Subscribe(ctx context.Context, channel chan<- container.Host) {
|
||||||
m.subscribers.Store(ctx, channel)
|
m.subscribers.Store(ctx, channel)
|
||||||
m.agentManager.Subscribe(ctx, channel)
|
m.agentManager.Subscribe(ctx, channel)
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ func (m *SwarmClientManager) RetryAndList() ([]ClientService, []error) {
|
|||||||
m.clients[host.ID] = client
|
m.clients[host.ID] = client
|
||||||
log.Info().Stringer("ip", ip).Str("id", host.ID).Str("name", host.Name).Msg("added new swarm agent")
|
log.Info().Stringer("ip", ip).Str("id", host.ID).Str("name", host.Name).Msg("added new swarm agent")
|
||||||
|
|
||||||
m.subscribers.Range(func(ctx context.Context, channel chan<- docker.Host) bool {
|
m.subscribers.Range(func(ctx context.Context, channel chan<- container.Host) bool {
|
||||||
host.Available = true
|
host.Available = true
|
||||||
host.Type = "swarm"
|
host.Type = "swarm"
|
||||||
|
|
||||||
@@ -205,12 +205,12 @@ func (m *SwarmClientManager) Find(id string) (ClientService, bool) {
|
|||||||
return client, ok
|
return client, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *SwarmClientManager) Hosts(ctx context.Context) []docker.Host {
|
func (m *SwarmClientManager) Hosts(ctx context.Context) []container.Host {
|
||||||
m.mu.RLock()
|
m.mu.RLock()
|
||||||
clients := lo.Values(m.clients)
|
clients := lo.Values(m.clients)
|
||||||
m.mu.RUnlock()
|
m.mu.RUnlock()
|
||||||
|
|
||||||
swarmNodes := lop.Map(clients, func(client ClientService, _ int) docker.Host {
|
swarmNodes := lop.Map(clients, func(client ClientService, _ int) container.Host {
|
||||||
host, err := client.Host(ctx)
|
host, err := client.Host(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn().Err(err).Str("id", host.ID).Msg("error getting host from client")
|
log.Warn().Err(err).Str("id", host.ID).Msg("error getting host from client")
|
||||||
@@ -230,6 +230,6 @@ func (m *SwarmClientManager) String() string {
|
|||||||
return fmt.Sprintf("SwarmClientManager{clients: %d}", len(m.clients))
|
return fmt.Sprintf("SwarmClientManager{clients: %d}", len(m.clients))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *SwarmClientManager) LocalClients() []docker.Client {
|
func (m *SwarmClientManager) LocalClients() []container.Client {
|
||||||
return []docker.Client{m.localClient}
|
return []container.Client{m.localClient}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
orderedmap "github.com/wk8/go-ordered-map/v2"
|
orderedmap "github.com/wk8/go-ordered-map/v2"
|
||||||
)
|
)
|
||||||
@@ -26,7 +26,7 @@ func ParseRegex(search string) (*regexp.Regexp, error) {
|
|||||||
return re, nil
|
return re, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Search(re *regexp.Regexp, logEvent *docker.LogEvent) bool {
|
func Search(re *regexp.Regexp, logEvent *container.LogEvent) bool {
|
||||||
switch value := logEvent.Message.(type) {
|
switch value := logEvent.Message.(type) {
|
||||||
case string:
|
case string:
|
||||||
if re.MatchString(value) {
|
if re.MatchString(value) {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/auth"
|
"github.com/amir20/dozzle/internal/auth"
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
@@ -30,7 +30,7 @@ func (h *handler) containerActions(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedAction, err := docker.ParseContainerAction(action)
|
parsedAction, err := container.ParseContainerAction(action)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("error while trying to parse action")
|
log.Error().Err(err).Msg("error while trying to parse action")
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -15,17 +15,17 @@ import (
|
|||||||
|
|
||||||
func mockedClient() *MockedClient {
|
func mockedClient() *MockedClient {
|
||||||
mockedClient := new(MockedClient)
|
mockedClient := new(MockedClient)
|
||||||
container := docker.Container{ID: "123"}
|
c := container.Container{ID: "123"}
|
||||||
|
|
||||||
mockedClient.On("FindContainer", mock.Anything, "123").Return(container, nil)
|
mockedClient.On("FindContainer", mock.Anything, "123").Return(c, nil)
|
||||||
mockedClient.On("FindContainer", mock.Anything, "456").Return(docker.Container{}, errors.New("container not found"))
|
mockedClient.On("FindContainer", mock.Anything, "456").Return(container.Container{}, errors.New("container not found"))
|
||||||
mockedClient.On("ContainerActions", mock.Anything, docker.Start, container.ID).Return(nil)
|
mockedClient.On("ContainerActions", mock.Anything, container.Start, c.ID).Return(nil)
|
||||||
mockedClient.On("ContainerActions", mock.Anything, docker.Stop, container.ID).Return(nil)
|
mockedClient.On("ContainerActions", mock.Anything, container.Stop, c.ID).Return(nil)
|
||||||
mockedClient.On("ContainerActions", mock.Anything, docker.Restart, container.ID).Return(nil)
|
mockedClient.On("ContainerActions", mock.Anything, container.Restart, c.ID).Return(nil)
|
||||||
mockedClient.On("ContainerActions", mock.Anything, docker.Start, mock.Anything).Return(errors.New("container not found"))
|
mockedClient.On("ContainerActions", mock.Anything, container.Start, mock.Anything).Return(errors.New("container not found"))
|
||||||
mockedClient.On("ContainerActions", mock.Anything, docker.ContainerAction("something-else"), container.ID).Return(errors.New("unknown action"))
|
mockedClient.On("ContainerActions", mock.Anything, container.ContainerAction("something-else"), c.ID).Return(errors.New("unknown action"))
|
||||||
mockedClient.On("Host").Return(docker.Host{ID: "localhost"})
|
mockedClient.On("Host").Return(container.Host{ID: "localhost"})
|
||||||
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]docker.Container{container}, nil)
|
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]container.Container{c}, nil)
|
||||||
mockedClient.On("ContainerEvents", mock.Anything, mock.Anything).Return(nil)
|
mockedClient.On("ContainerEvents", mock.Anything, mock.Anything).Return(nil)
|
||||||
|
|
||||||
return mockedClient
|
return mockedClient
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *handler) debugStore(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) debugStore(w http.ResponseWriter, r *http.Request) {
|
||||||
respone := make(map[string]interface{})
|
respone := make(map[string]interface{})
|
||||||
respone["hosts"] = h.multiHostService.Hosts()
|
respone["hosts"] = h.multiHostService.Hosts()
|
||||||
containers, errors := h.multiHostService.ListAllContainers(docker.ContainerFilter{})
|
containers, errors := h.multiHostService.ListAllContainers(container.ContainerFilter{})
|
||||||
respone["containers"] = containers
|
respone["containers"] = containers
|
||||||
respone["errors"] = errors
|
respone["errors"] = errors
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/auth"
|
"github.com/amir20/dozzle/internal/auth"
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/docker/docker/pkg/stdcopy"
|
"github.com/docker/docker/pkg/stdcopy"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
)
|
)
|
||||||
@@ -32,12 +32,12 @@ func (h *handler) downloadLogs(w http.ResponseWriter, r *http.Request) {
|
|||||||
now := time.Now()
|
now := time.Now()
|
||||||
nowFmt := now.Format("2006-01-02T15-04-05")
|
nowFmt := now.Format("2006-01-02T15-04-05")
|
||||||
|
|
||||||
var stdTypes docker.StdType
|
var stdTypes container.StdType
|
||||||
if r.URL.Query().Has("stdout") {
|
if r.URL.Query().Has("stdout") {
|
||||||
stdTypes |= docker.STDOUT
|
stdTypes |= container.STDOUT
|
||||||
}
|
}
|
||||||
if r.URL.Query().Has("stderr") {
|
if r.URL.Query().Has("stderr") {
|
||||||
stdTypes |= docker.STDERR
|
stdTypes |= container.STDERR
|
||||||
}
|
}
|
||||||
|
|
||||||
if stdTypes == 0 {
|
if stdTypes == 0 {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -21,17 +21,17 @@ func Test_handler_download_logs(t *testing.T) {
|
|||||||
|
|
||||||
mockedClient := new(MockedClient)
|
mockedClient := new(MockedClient)
|
||||||
|
|
||||||
data := makeMessage("INFO Testing logs...", docker.STDOUT)
|
data := makeMessage("INFO Testing logs...", container.STDOUT)
|
||||||
|
|
||||||
mockedClient.On("FindContainer", mock.Anything, id).Return(docker.Container{ID: id, Tty: false}, nil)
|
mockedClient.On("FindContainer", mock.Anything, id).Return(container.Container{ID: id, Tty: false}, nil)
|
||||||
mockedClient.On("ContainerLogsBetweenDates", mock.Anything, id, mock.Anything, mock.Anything, docker.STDOUT).Return(io.NopCloser(bytes.NewReader(data)), nil)
|
mockedClient.On("ContainerLogsBetweenDates", mock.Anything, id, mock.Anything, mock.Anything, container.STDOUT).Return(io.NopCloser(bytes.NewReader(data)), nil)
|
||||||
mockedClient.On("Host").Return(docker.Host{
|
mockedClient.On("Host").Return(container.Host{
|
||||||
ID: "localhost",
|
ID: "localhost",
|
||||||
})
|
})
|
||||||
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(nil).Run(func(args mock.Arguments) {
|
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- container.ContainerEvent")).Return(nil).Run(func(args mock.Arguments) {
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
})
|
})
|
||||||
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]docker.Container{
|
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]container.Container{
|
||||||
{ID: id, Name: "test", State: "running"},
|
{ID: id, Name: "test", State: "running"},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/amir20/dozzle/internal/analytics"
|
"github.com/amir20/dozzle/internal/analytics"
|
||||||
"github.com/amir20/dozzle/internal/auth"
|
"github.com/amir20/dozzle/internal/auth"
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
docker_support "github.com/amir20/dozzle/internal/support/docker"
|
docker_support "github.com/amir20/dozzle/internal/support/docker"
|
||||||
support_web "github.com/amir20/dozzle/internal/support/web"
|
support_web "github.com/amir20/dozzle/internal/support/web"
|
||||||
"github.com/amir20/dozzle/types"
|
"github.com/amir20/dozzle/types"
|
||||||
@@ -20,9 +20,9 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
events := make(chan docker.ContainerEvent)
|
events := make(chan container.ContainerEvent)
|
||||||
stats := make(chan docker.ContainerStat)
|
stats := make(chan container.ContainerStat)
|
||||||
availableHosts := make(chan docker.Host)
|
availableHosts := make(chan container.Host)
|
||||||
|
|
||||||
h.multiHostService.SubscribeEventsAndStats(r.Context(), events, stats)
|
h.multiHostService.SubscribeEventsAndStats(r.Context(), events, stats)
|
||||||
h.multiHostService.SubscribeAvailableHosts(r.Context(), availableHosts)
|
h.multiHostService.SubscribeAvailableHosts(r.Context(), availableHosts)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
docker_support "github.com/amir20/dozzle/internal/support/docker"
|
docker_support "github.com/amir20/dozzle/internal/support/docker"
|
||||||
"github.com/amir20/dozzle/internal/utils"
|
"github.com/amir20/dozzle/internal/utils"
|
||||||
"github.com/beme/abide"
|
"github.com/beme/abide"
|
||||||
@@ -24,17 +24,17 @@ func Test_handler_streamEvents_happy(t *testing.T) {
|
|||||||
|
|
||||||
mockedClient := new(MockedClient)
|
mockedClient := new(MockedClient)
|
||||||
|
|
||||||
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]docker.Container{}, nil)
|
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]container.Container{}, nil)
|
||||||
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(nil).Run(func(args mock.Arguments) {
|
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- container.ContainerEvent")).Return(nil).Run(func(args mock.Arguments) {
|
||||||
messages := args.Get(1).(chan<- docker.ContainerEvent)
|
messages := args.Get(1).(chan<- container.ContainerEvent)
|
||||||
|
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
messages <- docker.ContainerEvent{
|
messages <- container.ContainerEvent{
|
||||||
Name: "start",
|
Name: "start",
|
||||||
ActorID: "1234",
|
ActorID: "1234",
|
||||||
Host: "localhost",
|
Host: "localhost",
|
||||||
}
|
}
|
||||||
messages <- docker.ContainerEvent{
|
messages <- container.ContainerEvent{
|
||||||
Name: "something-random",
|
Name: "something-random",
|
||||||
ActorID: "1234",
|
ActorID: "1234",
|
||||||
Host: "localhost",
|
Host: "localhost",
|
||||||
@@ -42,19 +42,19 @@ func Test_handler_streamEvents_happy(t *testing.T) {
|
|||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
cancel()
|
cancel()
|
||||||
})
|
})
|
||||||
mockedClient.On("FindContainer", mock.Anything, "1234").Return(docker.Container{
|
mockedClient.On("FindContainer", mock.Anything, "1234").Return(container.Container{
|
||||||
ID: "1234",
|
ID: "1234",
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Image: "test",
|
Image: "test",
|
||||||
Stats: utils.NewRingBuffer[docker.ContainerStat](300), // 300 seconds of stats
|
Stats: utils.NewRingBuffer[container.ContainerStat](300), // 300 seconds of stats
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
mockedClient.On("Host").Return(docker.Host{
|
mockedClient.On("Host").Return(container.Host{
|
||||||
ID: "localhost",
|
ID: "localhost",
|
||||||
})
|
})
|
||||||
|
|
||||||
// This is needed so that the server is initialized for store
|
// This is needed so that the server is initialized for store
|
||||||
manager := docker_support.NewRetriableClientManager(nil, 3*time.Second, tls.Certificate{}, docker_support.NewDockerClientService(mockedClient, docker.ContainerFilter{}))
|
manager := docker_support.NewRetriableClientManager(nil, 3*time.Second, tls.Certificate{}, docker_support.NewDockerClientService(mockedClient, container.ContainerFilter{}))
|
||||||
multiHostService := docker_support.NewMultiHostService(manager, 3*time.Second)
|
multiHostService := docker_support.NewMultiHostService(manager, 3*time.Second)
|
||||||
|
|
||||||
server := CreateServer(multiHostService, nil, Config{Base: "/", Authorization: Authorization{Provider: NONE}})
|
server := CreateServer(multiHostService, nil, Config{Base: "/", Authorization: Authorization{Provider: NONE}})
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ func (h *handler) healthcheck(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
clients := h.multiHostService.LocalClients()
|
clients := h.multiHostService.LocalClients()
|
||||||
for _, client := range clients {
|
for _, client := range clients {
|
||||||
if _, err := client.Ping(r.Context()); err != nil {
|
if err := client.Ping(r.Context()); err != nil {
|
||||||
log.Error().Err(err).Str("host", client.Host().Name).Msg("error pinging host")
|
log.Error().Err(err).Str("host", client.Host().Name).Msg("error pinging host")
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/auth"
|
"github.com/amir20/dozzle/internal/auth"
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/amir20/dozzle/internal/support/search"
|
"github.com/amir20/dozzle/internal/support/search"
|
||||||
support_web "github.com/amir20/dozzle/internal/support/web"
|
support_web "github.com/amir20/dozzle/internal/support/web"
|
||||||
"github.com/amir20/dozzle/internal/utils"
|
"github.com/amir20/dozzle/internal/utils"
|
||||||
@@ -34,12 +34,12 @@ func (h *handler) fetchLogsBetweenDates(w http.ResponseWriter, r *http.Request)
|
|||||||
to, _ := time.Parse(time.RFC3339Nano, r.URL.Query().Get("to"))
|
to, _ := time.Parse(time.RFC3339Nano, r.URL.Query().Get("to"))
|
||||||
id := chi.URLParam(r, "id")
|
id := chi.URLParam(r, "id")
|
||||||
|
|
||||||
var stdTypes docker.StdType
|
var stdTypes container.StdType
|
||||||
if r.URL.Query().Has("stdout") {
|
if r.URL.Query().Has("stdout") {
|
||||||
stdTypes |= docker.STDOUT
|
stdTypes |= container.STDOUT
|
||||||
}
|
}
|
||||||
if r.URL.Query().Has("stderr") {
|
if r.URL.Query().Has("stderr") {
|
||||||
stdTypes |= docker.STDERR
|
stdTypes |= container.STDERR
|
||||||
}
|
}
|
||||||
|
|
||||||
if stdTypes == 0 {
|
if stdTypes == 0 {
|
||||||
@@ -61,7 +61,7 @@ func (h *handler) fetchLogsBetweenDates(w http.ResponseWriter, r *http.Request)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer := utils.NewRingBuffer[*docker.LogEvent](500)
|
buffer := utils.NewRingBuffer[*container.LogEvent](500)
|
||||||
delta := max(to.Sub(from), time.Second*3)
|
delta := max(to.Sub(from), time.Second*3)
|
||||||
|
|
||||||
var regex *regexp.Regexp
|
var regex *regexp.Regexp
|
||||||
@@ -179,7 +179,7 @@ func (h *handler) fetchLogsBetweenDates(w http.ResponseWriter, r *http.Request)
|
|||||||
func (h *handler) streamContainerLogs(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) streamContainerLogs(w http.ResponseWriter, r *http.Request) {
|
||||||
id := chi.URLParam(r, "id")
|
id := chi.URLParam(r, "id")
|
||||||
|
|
||||||
h.streamLogsForContainers(w, r, func(container *docker.Container) bool {
|
h.streamLogsForContainers(w, r, func(container *container.Container) bool {
|
||||||
return container.ID == id && container.Host == hostKey(r)
|
return container.ID == id && container.Host == hostKey(r)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -192,14 +192,14 @@ func (h *handler) streamLogsMerged(w http.ResponseWriter, r *http.Request) {
|
|||||||
ids[id] = true
|
ids[id] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
h.streamLogsForContainers(w, r, func(container *docker.Container) bool {
|
h.streamLogsForContainers(w, r, func(container *container.Container) bool {
|
||||||
return ids[container.ID] && container.Host == hostKey(r)
|
return ids[container.ID] && container.Host == hostKey(r)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) streamServiceLogs(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) streamServiceLogs(w http.ResponseWriter, r *http.Request) {
|
||||||
service := chi.URLParam(r, "service")
|
service := chi.URLParam(r, "service")
|
||||||
h.streamLogsForContainers(w, r, func(container *docker.Container) bool {
|
h.streamLogsForContainers(w, r, func(container *container.Container) bool {
|
||||||
return container.State == "running" && container.Labels["com.docker.swarm.service.name"] == service
|
return container.State == "running" && container.Labels["com.docker.swarm.service.name"] == service
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -207,7 +207,7 @@ func (h *handler) streamServiceLogs(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (h *handler) streamGroupedLogs(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) streamGroupedLogs(w http.ResponseWriter, r *http.Request) {
|
||||||
group := chi.URLParam(r, "group")
|
group := chi.URLParam(r, "group")
|
||||||
|
|
||||||
h.streamLogsForContainers(w, r, func(container *docker.Container) bool {
|
h.streamLogsForContainers(w, r, func(container *container.Container) bool {
|
||||||
return container.State == "running" && container.Group == group
|
return container.State == "running" && container.Group == group
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -215,25 +215,25 @@ func (h *handler) streamGroupedLogs(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (h *handler) streamStackLogs(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) streamStackLogs(w http.ResponseWriter, r *http.Request) {
|
||||||
stack := chi.URLParam(r, "stack")
|
stack := chi.URLParam(r, "stack")
|
||||||
|
|
||||||
h.streamLogsForContainers(w, r, func(container *docker.Container) bool {
|
h.streamLogsForContainers(w, r, func(container *container.Container) bool {
|
||||||
return container.State == "running" && container.Labels["com.docker.stack.namespace"] == stack
|
return container.State == "running" && container.Labels["com.docker.stack.namespace"] == stack
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) streamHostLogs(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) streamHostLogs(w http.ResponseWriter, r *http.Request) {
|
||||||
host := hostKey(r)
|
host := hostKey(r)
|
||||||
h.streamLogsForContainers(w, r, func(container *docker.Container) bool {
|
h.streamLogsForContainers(w, r, func(container *container.Container) bool {
|
||||||
return container.State == "running" && container.Host == host
|
return container.State == "running" && container.Host == host
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) streamLogsForContainers(w http.ResponseWriter, r *http.Request, containerFilter ContainerFilter) {
|
func (h *handler) streamLogsForContainers(w http.ResponseWriter, r *http.Request, containerFilter ContainerFilter) {
|
||||||
var stdTypes docker.StdType
|
var stdTypes container.StdType
|
||||||
if r.URL.Query().Has("stdout") {
|
if r.URL.Query().Has("stdout") {
|
||||||
stdTypes |= docker.STDOUT
|
stdTypes |= container.STDOUT
|
||||||
}
|
}
|
||||||
if r.URL.Query().Has("stderr") {
|
if r.URL.Query().Has("stderr") {
|
||||||
stdTypes |= docker.STDERR
|
stdTypes |= container.STDERR
|
||||||
}
|
}
|
||||||
|
|
||||||
if stdTypes == 0 {
|
if stdTypes == 0 {
|
||||||
@@ -263,9 +263,9 @@ func (h *handler) streamLogsForContainers(w http.ResponseWriter, r *http.Request
|
|||||||
|
|
||||||
absoluteTime := time.Time{}
|
absoluteTime := time.Time{}
|
||||||
var regex *regexp.Regexp
|
var regex *regexp.Regexp
|
||||||
liveLogs := make(chan *docker.LogEvent)
|
liveLogs := make(chan *container.LogEvent)
|
||||||
events := make(chan *docker.ContainerEvent, 1)
|
events := make(chan *container.ContainerEvent, 1)
|
||||||
backfill := make(chan []*docker.LogEvent)
|
backfill := make(chan []*container.LogEvent)
|
||||||
|
|
||||||
levels := make(map[string]struct{})
|
levels := make(map[string]struct{})
|
||||||
for _, level := range r.URL.Query()["levels"] {
|
for _, level := range r.URL.Query()["levels"] {
|
||||||
@@ -287,7 +287,7 @@ func (h *handler) streamLogsForContainers(w http.ResponseWriter, r *http.Request
|
|||||||
delta := -10 * time.Second
|
delta := -10 * time.Second
|
||||||
to := absoluteTime
|
to := absoluteTime
|
||||||
for minimum > 0 {
|
for minimum > 0 {
|
||||||
events := make([]*docker.LogEvent, 0)
|
events := make([]*container.LogEvent, 0)
|
||||||
stillRunning := false
|
stillRunning := false
|
||||||
for _, container := range existingContainers {
|
for _, container := range existingContainers {
|
||||||
containerService, err := h.multiHostService.FindContainer(container.Host, container.ID, usersFilter)
|
containerService, err := h.multiHostService.FindContainer(container.Host, container.ID, usersFilter)
|
||||||
@@ -336,30 +336,30 @@ func (h *handler) streamLogsForContainers(w http.ResponseWriter, r *http.Request
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
streamLogs := func(container docker.Container) {
|
streamLogs := func(c container.Container) {
|
||||||
containerService, err := h.multiHostService.FindContainer(container.Host, container.ID, usersFilter)
|
containerService, err := h.multiHostService.FindContainer(c.Host, c.ID, usersFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("error while finding container")
|
log.Error().Err(err).Msg("error while finding container")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
container = containerService.Container
|
c = containerService.Container
|
||||||
start := utils.Max(absoluteTime, container.StartedAt)
|
start := utils.Max(absoluteTime, c.StartedAt)
|
||||||
err = containerService.StreamLogs(r.Context(), start, stdTypes, liveLogs)
|
err = containerService.StreamLogs(r.Context(), start, stdTypes, liveLogs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, io.EOF) {
|
if errors.Is(err, io.EOF) {
|
||||||
log.Debug().Str("container", container.ID).Msg("streaming ended")
|
log.Debug().Str("container", c.ID).Msg("streaming ended")
|
||||||
finishedAt := container.FinishedAt
|
finishedAt := c.FinishedAt
|
||||||
if container.FinishedAt.IsZero() {
|
if c.FinishedAt.IsZero() {
|
||||||
finishedAt = time.Now()
|
finishedAt = time.Now()
|
||||||
}
|
}
|
||||||
events <- &docker.ContainerEvent{
|
events <- &container.ContainerEvent{
|
||||||
ActorID: container.ID,
|
ActorID: c.ID,
|
||||||
Name: "container-stopped",
|
Name: "container-stopped",
|
||||||
Host: container.Host,
|
Host: c.Host,
|
||||||
Time: finishedAt,
|
Time: finishedAt,
|
||||||
}
|
}
|
||||||
} else if !errors.Is(err, context.Canceled) {
|
} else if !errors.Is(err, context.Canceled) {
|
||||||
log.Error().Err(err).Str("container", container.ID).Msg("unknown error while streaming logs")
|
log.Error().Err(err).Str("container", c.ID).Msg("unknown error while streaming logs")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -368,7 +368,7 @@ func (h *handler) streamLogsForContainers(w http.ResponseWriter, r *http.Request
|
|||||||
go streamLogs(container)
|
go streamLogs(container)
|
||||||
}
|
}
|
||||||
|
|
||||||
newContainers := make(chan docker.Container)
|
newContainers := make(chan container.Container)
|
||||||
h.multiHostService.SubscribeContainersStarted(r.Context(), newContainers, containerFilter)
|
h.multiHostService.SubscribeContainersStarted(r.Context(), newContainers, containerFilter)
|
||||||
|
|
||||||
ticker := time.NewTicker(5 * time.Second)
|
ticker := time.NewTicker(5 * time.Second)
|
||||||
@@ -387,10 +387,10 @@ loop:
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sseWriter.Message(logEvent)
|
sseWriter.Message(logEvent)
|
||||||
case container := <-newContainers:
|
case c := <-newContainers:
|
||||||
if _, err := h.multiHostService.FindContainer(container.Host, container.ID, usersFilter); err == nil {
|
if _, err := h.multiHostService.FindContainer(c.Host, c.ID, usersFilter); err == nil {
|
||||||
events <- &docker.ContainerEvent{ActorID: container.ID, Name: "container-started", Host: container.Host}
|
events <- &container.ContainerEvent{ActorID: c.ID, Name: "container-started", Host: c.Host}
|
||||||
go streamLogs(container)
|
go streamLogs(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
case event := <-events:
|
case event := <-events:
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/beme/abide"
|
"github.com/beme/abide"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -36,25 +36,25 @@ func Test_handler_streamLogs_happy(t *testing.T) {
|
|||||||
|
|
||||||
mockedClient := new(MockedClient)
|
mockedClient := new(MockedClient)
|
||||||
|
|
||||||
data := makeMessage("INFO Testing logs...", docker.STDOUT)
|
data := makeMessage("INFO Testing logs...", container.STDOUT)
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
mockedClient.On("FindContainer", mock.Anything, id).Return(docker.Container{ID: id, Tty: false, Host: "localhost", StartedAt: now}, nil)
|
mockedClient.On("FindContainer", mock.Anything, id).Return(container.Container{ID: id, Tty: false, Host: "localhost", StartedAt: now}, nil)
|
||||||
mockedClient.On("ContainerLogs", mock.Anything, mock.Anything, now, docker.STDALL).Return(io.NopCloser(bytes.NewReader(data)), nil).
|
mockedClient.On("ContainerLogs", mock.Anything, mock.Anything, now, container.STDALL).Return(io.NopCloser(bytes.NewReader(data)), nil).
|
||||||
Run(func(args mock.Arguments) {
|
Run(func(args mock.Arguments) {
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
cancel()
|
cancel()
|
||||||
}()
|
}()
|
||||||
})
|
})
|
||||||
mockedClient.On("Host").Return(docker.Host{
|
mockedClient.On("Host").Return(container.Host{
|
||||||
ID: "localhost",
|
ID: "localhost",
|
||||||
})
|
})
|
||||||
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]docker.Container{
|
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]container.Container{
|
||||||
{ID: id, Name: "test", Host: "localhost", State: "running"},
|
{ID: id, Name: "test", Host: "localhost", State: "running"},
|
||||||
}, nil)
|
}, nil)
|
||||||
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(nil).Run(func(args mock.Arguments) {
|
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- container.ContainerEvent")).Return(nil).Run(func(args mock.Arguments) {
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -80,27 +80,27 @@ func Test_handler_streamLogs_happy_with_id(t *testing.T) {
|
|||||||
|
|
||||||
mockedClient := new(MockedClient)
|
mockedClient := new(MockedClient)
|
||||||
|
|
||||||
data := makeMessage("2020-05-13T18:55:37.772853839Z INFO Testing logs...", docker.STDOUT)
|
data := makeMessage("2020-05-13T18:55:37.772853839Z INFO Testing logs...", container.STDOUT)
|
||||||
|
|
||||||
started := time.Date(2020, time.May, 13, 18, 55, 37, 772853839, time.UTC)
|
started := time.Date(2020, time.May, 13, 18, 55, 37, 772853839, time.UTC)
|
||||||
|
|
||||||
mockedClient.On("FindContainer", mock.Anything, id).Return(docker.Container{ID: id, Host: "localhost", StartedAt: started}, nil)
|
mockedClient.On("FindContainer", mock.Anything, id).Return(container.Container{ID: id, Host: "localhost", StartedAt: started}, nil)
|
||||||
mockedClient.On("ContainerLogs", mock.Anything, mock.Anything, started, docker.STDALL).Return(io.NopCloser(bytes.NewReader(data)), nil).
|
mockedClient.On("ContainerLogs", mock.Anything, mock.Anything, started, container.STDALL).Return(io.NopCloser(bytes.NewReader(data)), nil).
|
||||||
Run(func(args mock.Arguments) {
|
Run(func(args mock.Arguments) {
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
cancel()
|
cancel()
|
||||||
}()
|
}()
|
||||||
})
|
})
|
||||||
mockedClient.On("Host").Return(docker.Host{
|
mockedClient.On("Host").Return(container.Host{
|
||||||
ID: "localhost",
|
ID: "localhost",
|
||||||
})
|
})
|
||||||
|
|
||||||
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]docker.Container{
|
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]container.Container{
|
||||||
{ID: id, Name: "test", Host: "localhost", State: "running"},
|
{ID: id, Name: "test", Host: "localhost", State: "running"},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(nil).Run(func(args mock.Arguments) {
|
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- container.ContainerEvent")).Return(nil).Run(func(args mock.Arguments) {
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -125,21 +125,21 @@ func Test_handler_streamLogs_happy_container_stopped(t *testing.T) {
|
|||||||
|
|
||||||
started := time.Date(2020, time.May, 13, 18, 55, 37, 772853839, time.UTC)
|
started := time.Date(2020, time.May, 13, 18, 55, 37, 772853839, time.UTC)
|
||||||
mockedClient := new(MockedClient)
|
mockedClient := new(MockedClient)
|
||||||
mockedClient.On("FindContainer", mock.Anything, id).Return(docker.Container{ID: id, Host: "localhost", StartedAt: started}, nil)
|
mockedClient.On("FindContainer", mock.Anything, id).Return(container.Container{ID: id, Host: "localhost", StartedAt: started}, nil)
|
||||||
mockedClient.On("ContainerLogs", mock.Anything, id, started, docker.STDALL).Return(io.NopCloser(strings.NewReader("")), io.EOF).
|
mockedClient.On("ContainerLogs", mock.Anything, id, started, container.STDALL).Return(io.NopCloser(strings.NewReader("")), io.EOF).
|
||||||
Run(func(args mock.Arguments) {
|
Run(func(args mock.Arguments) {
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
cancel()
|
cancel()
|
||||||
}()
|
}()
|
||||||
})
|
})
|
||||||
mockedClient.On("Host").Return(docker.Host{
|
mockedClient.On("Host").Return(container.Host{
|
||||||
ID: "localhost",
|
ID: "localhost",
|
||||||
})
|
})
|
||||||
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]docker.Container{
|
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]container.Container{
|
||||||
{ID: id, Name: "test", Host: "localhost", State: "running"},
|
{ID: id, Name: "test", Host: "localhost", State: "running"},
|
||||||
}, nil)
|
}, nil)
|
||||||
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(nil)
|
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- container.ContainerEvent")).Return(nil)
|
||||||
|
|
||||||
handler := createDefaultHandler(mockedClient)
|
handler := createDefaultHandler(mockedClient)
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
@@ -163,21 +163,21 @@ func Test_handler_streamLogs_error_reading(t *testing.T) {
|
|||||||
|
|
||||||
started := time.Date(2020, time.May, 13, 18, 55, 37, 772853839, time.UTC)
|
started := time.Date(2020, time.May, 13, 18, 55, 37, 772853839, time.UTC)
|
||||||
mockedClient := new(MockedClient)
|
mockedClient := new(MockedClient)
|
||||||
mockedClient.On("FindContainer", mock.Anything, id).Return(docker.Container{ID: id, Host: "localhost", StartedAt: started}, nil)
|
mockedClient.On("FindContainer", mock.Anything, id).Return(container.Container{ID: id, Host: "localhost", StartedAt: started}, nil)
|
||||||
mockedClient.On("ContainerLogs", mock.Anything, id, started, docker.STDALL).Return(io.NopCloser(strings.NewReader("")), errors.New("test error")).
|
mockedClient.On("ContainerLogs", mock.Anything, id, started, container.STDALL).Return(io.NopCloser(strings.NewReader("")), errors.New("test error")).
|
||||||
Run(func(args mock.Arguments) {
|
Run(func(args mock.Arguments) {
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
cancel()
|
cancel()
|
||||||
}()
|
}()
|
||||||
})
|
})
|
||||||
mockedClient.On("Host").Return(docker.Host{
|
mockedClient.On("Host").Return(container.Host{
|
||||||
ID: "localhost",
|
ID: "localhost",
|
||||||
})
|
})
|
||||||
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]docker.Container{
|
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]container.Container{
|
||||||
{ID: id, Name: "test", Host: "localhost", State: "running"},
|
{ID: id, Name: "test", Host: "localhost", State: "running"},
|
||||||
}, nil)
|
}, nil)
|
||||||
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(nil)
|
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- container.ContainerEvent")).Return(nil)
|
||||||
|
|
||||||
handler := createDefaultHandler(mockedClient)
|
handler := createDefaultHandler(mockedClient)
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
@@ -194,14 +194,14 @@ func Test_handler_streamLogs_error_std(t *testing.T) {
|
|||||||
require.NoError(t, err, "NewRequest should not return an error.")
|
require.NoError(t, err, "NewRequest should not return an error.")
|
||||||
|
|
||||||
mockedClient := new(MockedClient)
|
mockedClient := new(MockedClient)
|
||||||
mockedClient.On("FindContainer", mock.Anything, id).Return(docker.Container{ID: id, Host: "localhost"}, nil)
|
mockedClient.On("FindContainer", mock.Anything, id).Return(container.Container{ID: id, Host: "localhost"}, nil)
|
||||||
mockedClient.On("Host").Return(docker.Host{
|
mockedClient.On("Host").Return(container.Host{
|
||||||
ID: "localhost",
|
ID: "localhost",
|
||||||
})
|
})
|
||||||
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]docker.Container{
|
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]container.Container{
|
||||||
{ID: id, Name: "test", Host: "localhost", State: "running"},
|
{ID: id, Name: "test", Host: "localhost", State: "running"},
|
||||||
}, nil)
|
}, nil)
|
||||||
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(nil).
|
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- container.ContainerEvent")).Return(nil).
|
||||||
Run(func(args mock.Arguments) {
|
Run(func(args mock.Arguments) {
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
})
|
})
|
||||||
@@ -231,19 +231,19 @@ func Test_handler_between_dates(t *testing.T) {
|
|||||||
|
|
||||||
mockedClient := new(MockedClient)
|
mockedClient := new(MockedClient)
|
||||||
|
|
||||||
first := makeMessage("2020-05-13T18:55:37.772853839Z INFO Testing stdout logs...\n", docker.STDOUT)
|
first := makeMessage("2020-05-13T18:55:37.772853839Z INFO Testing stdout logs...\n", container.STDOUT)
|
||||||
second := makeMessage("2020-05-13T18:56:37.772853839Z INFO Testing stderr logs...\n", docker.STDERR)
|
second := makeMessage("2020-05-13T18:56:37.772853839Z INFO Testing stderr logs...\n", container.STDERR)
|
||||||
data := append(first, second...)
|
data := append(first, second...)
|
||||||
|
|
||||||
mockedClient.On("ContainerLogsBetweenDates", mock.Anything, id, from, to, docker.STDALL).Return(io.NopCloser(bytes.NewReader(data)), nil)
|
mockedClient.On("ContainerLogsBetweenDates", mock.Anything, id, from, to, container.STDALL).Return(io.NopCloser(bytes.NewReader(data)), nil)
|
||||||
mockedClient.On("FindContainer", mock.Anything, id).Return(docker.Container{ID: id}, nil)
|
mockedClient.On("FindContainer", mock.Anything, id).Return(container.Container{ID: id}, nil)
|
||||||
mockedClient.On("Host").Return(docker.Host{
|
mockedClient.On("Host").Return(container.Host{
|
||||||
ID: "localhost",
|
ID: "localhost",
|
||||||
})
|
})
|
||||||
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]docker.Container{
|
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]container.Container{
|
||||||
{ID: id, Name: "test", Host: "localhost", State: "running"},
|
{ID: id, Name: "test", Host: "localhost", State: "running"},
|
||||||
}, nil)
|
}, nil)
|
||||||
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(nil)
|
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- container.ContainerEvent")).Return(nil)
|
||||||
|
|
||||||
handler := createDefaultHandler(mockedClient)
|
handler := createDefaultHandler(mockedClient)
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
@@ -273,24 +273,24 @@ func Test_handler_between_dates_with_fill(t *testing.T) {
|
|||||||
|
|
||||||
mockedClient := new(MockedClient)
|
mockedClient := new(MockedClient)
|
||||||
|
|
||||||
first := makeMessage("2020-05-13T18:55:37.772853839Z INFO Testing stdout logs...\n", docker.STDOUT)
|
first := makeMessage("2020-05-13T18:55:37.772853839Z INFO Testing stdout logs...\n", container.STDOUT)
|
||||||
second := makeMessage("2020-05-13T18:56:37.772853839Z INFO Testing stderr logs...\n", docker.STDERR)
|
second := makeMessage("2020-05-13T18:56:37.772853839Z INFO Testing stderr logs...\n", container.STDERR)
|
||||||
data := append(first, second...)
|
data := append(first, second...)
|
||||||
|
|
||||||
mockedClient.On("ContainerLogsBetweenDates", mock.Anything, id, from, to, docker.STDALL).
|
mockedClient.On("ContainerLogsBetweenDates", mock.Anything, id, from, to, container.STDALL).
|
||||||
Return(io.NopCloser(bytes.NewReader([]byte{})), nil).
|
Return(io.NopCloser(bytes.NewReader([]byte{})), nil).
|
||||||
Once()
|
Once()
|
||||||
mockedClient.On("ContainerLogsBetweenDates", mock.Anything, id, time.Date(2017, time.December, 31, 14, 0, 0, 0, time.UTC), to, docker.STDALL).
|
mockedClient.On("ContainerLogsBetweenDates", mock.Anything, id, time.Date(2017, time.December, 31, 14, 0, 0, 0, time.UTC), to, container.STDALL).
|
||||||
Return(io.NopCloser(bytes.NewReader(data)), nil).
|
Return(io.NopCloser(bytes.NewReader(data)), nil).
|
||||||
Once()
|
Once()
|
||||||
mockedClient.On("FindContainer", mock.Anything, id).Return(docker.Container{ID: id}, nil)
|
mockedClient.On("FindContainer", mock.Anything, id).Return(container.Container{ID: id}, nil)
|
||||||
mockedClient.On("Host").Return(docker.Host{
|
mockedClient.On("Host").Return(container.Host{
|
||||||
ID: "localhost",
|
ID: "localhost",
|
||||||
})
|
})
|
||||||
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]docker.Container{
|
mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]container.Container{
|
||||||
{ID: id, Name: "test", Host: "localhost", State: "running"},
|
{ID: id, Name: "test", Host: "localhost", State: "running"},
|
||||||
}, nil)
|
}, nil)
|
||||||
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(nil)
|
mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- container.ContainerEvent")).Return(nil)
|
||||||
|
|
||||||
handler := createDefaultHandler(mockedClient)
|
handler := createDefaultHandler(mockedClient)
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
@@ -300,7 +300,7 @@ func Test_handler_between_dates_with_fill(t *testing.T) {
|
|||||||
mockedClient.AssertExpectations(t)
|
mockedClient.AssertExpectations(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeMessage(message string, stream docker.StdType) []byte {
|
func makeMessage(message string, stream container.StdType) []byte {
|
||||||
data := make([]byte, 8)
|
data := make([]byte, 8)
|
||||||
binary.BigEndian.PutUint32(data[4:], uint32(len(message)))
|
binary.BigEndian.PutUint32(data[4:], uint32(len(message)))
|
||||||
data[0] = byte(stream / 2)
|
data[0] = byte(stream / 2)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/auth"
|
"github.com/amir20/dozzle/internal/auth"
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
docker_support "github.com/amir20/dozzle/internal/support/docker"
|
docker_support "github.com/amir20/dozzle/internal/support/docker"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
@@ -34,7 +34,7 @@ type Config struct {
|
|||||||
Dev bool
|
Dev bool
|
||||||
Authorization Authorization
|
Authorization Authorization
|
||||||
EnableActions bool
|
EnableActions bool
|
||||||
Filter docker.ContainerFilter
|
Filter container.ContainerFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
type Authorization struct {
|
type Authorization struct {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/container"
|
||||||
docker_support "github.com/amir20/dozzle/internal/support/docker"
|
docker_support "github.com/amir20/dozzle/internal/support/docker"
|
||||||
"github.com/docker/docker/api/types/system"
|
"github.com/docker/docker/api/types/system"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
@@ -20,46 +20,46 @@ import (
|
|||||||
|
|
||||||
type MockedClient struct {
|
type MockedClient struct {
|
||||||
mock.Mock
|
mock.Mock
|
||||||
docker.Client
|
container.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) FindContainer(ctx context.Context, id string) (docker.Container, error) {
|
func (m *MockedClient) FindContainer(ctx context.Context, id string) (container.Container, error) {
|
||||||
args := m.Called(ctx, id)
|
args := m.Called(ctx, id)
|
||||||
return args.Get(0).(docker.Container), args.Error(1)
|
return args.Get(0).(container.Container), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) ContainerActions(ctx context.Context, action docker.ContainerAction, containerID string) error {
|
func (m *MockedClient) ContainerActions(ctx context.Context, action container.ContainerAction, containerID string) error {
|
||||||
args := m.Called(ctx, action, containerID)
|
args := m.Called(ctx, action, containerID)
|
||||||
return args.Error(0)
|
return args.Error(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) ContainerEvents(ctx context.Context, events chan<- docker.ContainerEvent) error {
|
func (m *MockedClient) ContainerEvents(ctx context.Context, events chan<- container.ContainerEvent) error {
|
||||||
args := m.Called(ctx, events)
|
args := m.Called(ctx, events)
|
||||||
return args.Error(0)
|
return args.Error(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) ListContainers(ctx context.Context, filter docker.ContainerFilter) ([]docker.Container, error) {
|
func (m *MockedClient) ListContainers(ctx context.Context, filter container.ContainerFilter) ([]container.Container, error) {
|
||||||
args := m.Called(ctx, filter)
|
args := m.Called(ctx, filter)
|
||||||
return args.Get(0).([]docker.Container), args.Error(1)
|
return args.Get(0).([]container.Container), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) ContainerLogs(ctx context.Context, id string, since time.Time, stdType docker.StdType) (io.ReadCloser, error) {
|
func (m *MockedClient) ContainerLogs(ctx context.Context, id string, since time.Time, stdType container.StdType) (io.ReadCloser, error) {
|
||||||
args := m.Called(ctx, id, since, stdType)
|
args := m.Called(ctx, id, since, stdType)
|
||||||
return args.Get(0).(io.ReadCloser), args.Error(1)
|
return args.Get(0).(io.ReadCloser), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) ContainerStats(context.Context, string, chan<- docker.ContainerStat) error {
|
func (m *MockedClient) ContainerStats(context.Context, string, chan<- container.ContainerStat) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) ContainerLogsBetweenDates(ctx context.Context, id string, from time.Time, to time.Time, stdType docker.StdType) (io.ReadCloser, error) {
|
func (m *MockedClient) ContainerLogsBetweenDates(ctx context.Context, id string, from time.Time, to time.Time, stdType container.StdType) (io.ReadCloser, error) {
|
||||||
args := m.Called(ctx, id, from, to, stdType)
|
args := m.Called(ctx, id, from, to, stdType)
|
||||||
return args.Get(0).(io.ReadCloser), args.Error(1)
|
return args.Get(0).(io.ReadCloser), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) Host() docker.Host {
|
func (m *MockedClient) Host() container.Host {
|
||||||
args := m.Called()
|
args := m.Called()
|
||||||
return args.Get(0).(docker.Host)
|
return args.Get(0).(container.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) IsSwarmMode() bool {
|
func (m *MockedClient) IsSwarmMode() bool {
|
||||||
@@ -70,14 +70,14 @@ func (m *MockedClient) SystemInfo() system.Info {
|
|||||||
return system.Info{ID: "123"}
|
return system.Info{ID: "123"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createHandler(client docker.Client, content fs.FS, config Config) *chi.Mux {
|
func createHandler(client container.Client, content fs.FS, config Config) *chi.Mux {
|
||||||
if client == nil {
|
if client == nil {
|
||||||
client = new(MockedClient)
|
client = new(MockedClient)
|
||||||
client.(*MockedClient).On("ListContainers", mock.Anything, mock.Anything).Return([]docker.Container{}, nil)
|
client.(*MockedClient).On("ListContainers", mock.Anything, mock.Anything).Return([]container.Container{}, nil)
|
||||||
client.(*MockedClient).On("Host").Return(docker.Host{
|
client.(*MockedClient).On("Host").Return(container.Host{
|
||||||
ID: "localhost",
|
ID: "localhost",
|
||||||
})
|
})
|
||||||
client.(*MockedClient).On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(nil)
|
client.(*MockedClient).On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- container.ContainerEvent")).Return(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if content == nil {
|
if content == nil {
|
||||||
@@ -86,7 +86,7 @@ func createHandler(client docker.Client, content fs.FS, config Config) *chi.Mux
|
|||||||
content = afero.NewIOFS(fs)
|
content = afero.NewIOFS(fs)
|
||||||
}
|
}
|
||||||
|
|
||||||
manager := docker_support.NewRetriableClientManager(nil, 3*time.Second, tls.Certificate{}, docker_support.NewDockerClientService(client, docker.ContainerFilter{}))
|
manager := docker_support.NewRetriableClientManager(nil, 3*time.Second, tls.Certificate{}, docker_support.NewDockerClientService(client, container.ContainerFilter{}))
|
||||||
multiHostService := docker_support.NewMultiHostService(manager, 3*time.Second)
|
multiHostService := docker_support.NewMultiHostService(manager, 3*time.Second)
|
||||||
return createRouter(&handler{
|
return createRouter(&handler{
|
||||||
multiHostService: multiHostService,
|
multiHostService: multiHostService,
|
||||||
@@ -95,6 +95,6 @@ func createHandler(client docker.Client, content fs.FS, config Config) *chi.Mux
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func createDefaultHandler(client docker.Client) *chi.Mux {
|
func createDefaultHandler(client container.Client) *chi.Mux {
|
||||||
return createHandler(client, nil, Config{Base: "/", Authorization: Authorization{Provider: NONE}})
|
return createHandler(client, nil, Config{Base: "/", Authorization: Authorization{Provider: NONE}})
|
||||||
}
|
}
|
||||||
|
|||||||
3
main.go
3
main.go
@@ -16,6 +16,7 @@ import (
|
|||||||
|
|
||||||
"github.com/amir20/dozzle/internal/agent"
|
"github.com/amir20/dozzle/internal/agent"
|
||||||
"github.com/amir20/dozzle/internal/auth"
|
"github.com/amir20/dozzle/internal/auth"
|
||||||
|
"github.com/amir20/dozzle/internal/container"
|
||||||
"github.com/amir20/dozzle/internal/docker"
|
"github.com/amir20/dozzle/internal/docker"
|
||||||
"github.com/amir20/dozzle/internal/healthcheck"
|
"github.com/amir20/dozzle/internal/healthcheck"
|
||||||
"github.com/amir20/dozzle/internal/support/cli"
|
"github.com/amir20/dozzle/internal/support/cli"
|
||||||
@@ -161,7 +162,7 @@ func main() {
|
|||||||
|
|
||||||
var multiHostService *docker_support.MultiHostService
|
var multiHostService *docker_support.MultiHostService
|
||||||
if args.Mode == "server" {
|
if args.Mode == "server" {
|
||||||
var localClient docker.Client
|
var localClient container.Client
|
||||||
localClient, multiHostService = cli.CreateMultiHostService(certs, args)
|
localClient, multiHostService = cli.CreateMultiHostService(certs, args)
|
||||||
if multiHostService.TotalClients() == 0 {
|
if multiHostService.TotalClients() == 0 {
|
||||||
log.Fatal().Msg("Could not connect to any Docker Engine")
|
log.Fatal().Msg("Could not connect to any Docker Engine")
|
||||||
|
|||||||
Reference in New Issue
Block a user