diff --git a/internal/agent/client.go b/internal/agent/client.go index eb02b1e9..4af967bb 100644 --- a/internal/agent/client.go +++ b/internal/agent/client.go @@ -13,7 +13,7 @@ import ( "encoding/json" "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/rs/zerolog/log" 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{ ContainerId: containerID, Since: timestamppb.New(since), @@ -101,7 +101,7 @@ func (c *Client) LogsBetweenDates(ctx context.Context, containerID string, since return nil, err } - events := make(chan *docker.LogEvent) + events := make(chan *container.LogEvent) go func() { sendLogs(stream, events) @@ -111,7 +111,7 @@ func (c *Client) LogsBetweenDates(ctx context.Context, containerID string, since 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{ ContainerId: containerID, Since: timestamppb.New(since), @@ -125,7 +125,7 @@ func (c *Client) StreamContainerLogs(ctx context.Context, containerID string, si 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 { resp, err := stream.Recv() if err != nil { @@ -151,19 +151,19 @@ func sendLogs(stream pb.AgentService_StreamLogsClient, events chan<- *docker.Log continue } - events <- &docker.LogEvent{ + events <- &container.LogEvent{ Id: resp.Event.Id, ContainerID: resp.Event.ContainerId, Message: message, Timestamp: resp.Event.Timestamp.AsTime().Unix(), - Position: docker.LogPosition(resp.Event.Position), + Position: container.LogPosition(resp.Event.Position), Level: resp.Event.Level, 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{ ContainerId: containerID, Since: timestamppb.New(since), @@ -199,7 +199,7 @@ func (c *Client) StreamRawBytes(ctx context.Context, containerID string, since t 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{}) if err != nil { return err @@ -211,7 +211,7 @@ func (c *Client) StreamStats(ctx context.Context, stats chan<- docker.ContainerS return rpcErrToErr(err) } - stats <- docker.ContainerStat{ + stats <- container.ContainerStat{ CPUPercent: resp.Stat.CpuPercent, MemoryPercent: resp.Stat.MemoryPercent, 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{}) if err != nil { return err @@ -232,7 +232,7 @@ func (c *Client) StreamEvents(ctx context.Context, events chan<- docker.Containe return rpcErrToErr(err) } - events <- docker.ContainerEvent{ + events <- container.ContainerEvent{ ActorID: resp.Event.ActorId, Name: resp.Event.Name, 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{}) if err != nil { return err @@ -253,7 +253,7 @@ func (c *Client) StreamNewContainers(ctx context.Context, containers chan<- dock return rpcErrToErr(err) } - containers <- docker.Container{ + containers <- container.Container{ ID: resp.Container.Id, Name: resp.Container.Name, 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}) 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 { - stats = append(stats, docker.ContainerStat{ + stats = append(stats, container.ContainerStat{ ID: stat.Id, CPUPercent: stat.CpuPercent, 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, Name: response.Container.Name, Image: response.Container.Image, @@ -306,7 +306,7 @@ func (c *Client) FindContainer(ctx context.Context, containerID string) (docker. }, 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{} if filter != nil { @@ -321,11 +321,11 @@ func (c *Client) ListContainers(ctx context.Context, filter docker.ContainerFilt return nil, err } - containers := make([]docker.Container, 0) - for _, container := range response.Containers { - var stats []docker.ContainerStat - for _, stat := range container.Stats { - stats = append(stats, docker.ContainerStat{ + containers := make([]container.Container, 0) + for _, c := range response.Containers { + var stats []container.ContainerStat + for _, stat := range c.Stats { + stats = append(stats, container.ContainerStat{ ID: stat.Id, CPUPercent: stat.CpuPercent, MemoryPercent: stat.MemoryPercent, @@ -333,20 +333,20 @@ func (c *Client) ListContainers(ctx context.Context, filter docker.ContainerFilt }) } - containers = append(containers, docker.Container{ - ID: container.Id, - Name: container.Name, - Image: container.Image, - Labels: container.Labels, - Group: container.Group, - Created: container.Created.AsTime(), - State: container.State, - Health: container.Health, - Host: container.Host, - Tty: container.Tty, - Command: container.Command, - StartedAt: container.Started.AsTime(), - FinishedAt: container.Finished.AsTime(), + containers = append(containers, container.Container{ + ID: c.Id, + Name: c.Name, + Image: c.Image, + Labels: c.Labels, + Group: c.Group, + Created: c.Created.AsTime(), + State: c.State, + Health: c.Health, + Host: c.Host, + Tty: c.Tty, + Command: c.Command, + StartedAt: c.Started.AsTime(), + FinishedAt: c.Finished.AsTime(), Stats: utils.RingBufferFrom(300, stats), }) } @@ -354,17 +354,17 @@ func (c *Client) ListContainers(ctx context.Context, filter docker.ContainerFilt 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{}) if err != nil { - return docker.Host{ + return container.Host{ Endpoint: c.endpoint, Type: "agent", Available: false, }, err } - return docker.Host{ + return container.Host{ ID: info.Host.Id, Name: info.Host.Name, NCPU: int(info.Host.CpuCores), @@ -376,16 +376,16 @@ func (c *Client) Host(ctx context.Context) (docker.Host, error) { }, 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 switch action { - case docker.Start: + case container.Start: containerAction = pb.ContainerAction_Start - case docker.Stop: + case container.Stop: containerAction = pb.ContainerAction_Stop - case docker.Restart: + case container.Restart: containerAction = pb.ContainerAction_Restart } diff --git a/internal/agent/client_test.go b/internal/agent/client_test.go index b2082c5b..3c87514d 100644 --- a/internal/agent/client_test.go +++ b/internal/agent/client_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - "github.com/amir20/dozzle/internal/docker" + "github.com/amir20/dozzle/internal/container" "github.com/amir20/dozzle/internal/utils" "github.com/docker/docker/api/types/system" "github.com/stretchr/testify/assert" @@ -28,46 +28,46 @@ var client *MockedClient type MockedClient struct { 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) - 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) 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) 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) - 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) 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 } -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) return args.Get(0).(io.ReadCloser), args.Error(1) } -func (m *MockedClient) Host() docker.Host { +func (m *MockedClient) Host() container.Host { args := m.Called() - return args.Get(0).(docker.Host) + return args.Get(0).(container.Host) } func (m *MockedClient) IsSwarmMode() bool { @@ -92,7 +92,7 @@ func init() { } client = &MockedClient{} - client.On("ListContainers", mock.Anything, mock.Anything).Return([]docker.Container{ + client.On("ListContainers", mock.Anything, mock.Anything).Return([]container.Container{ { ID: "123456", Name: "test", @@ -101,17 +101,17 @@ func init() { }, }, nil) - client.On("Host").Return(docker.Host{ + client.On("Host").Return(container.Host{ ID: "localhost", Endpoint: "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) }) - client.On("FindContainer", mock.Anything, "123456").Return(docker.Container{ + client.On("FindContainer", mock.Anything, "123456").Return(container.Container{ ID: "123456", Name: "test", Host: "localhost", @@ -124,13 +124,13 @@ func init() { Labels: map[string]string{ "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), StartedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), FinishedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), }, nil) - server, _ := NewServer(client, certs, "test", docker.ContainerFilter{}) + server, _ := NewServer(client, certs, "test", container.ContainerFilter{}) go server.Serve(lis) } @@ -145,9 +145,9 @@ func TestFindContainer(t *testing.T) { 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", Name: "test", Host: "localhost", @@ -160,7 +160,7 @@ func TestFindContainer(t *testing.T) { Labels: map[string]string{ "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), StartedAt: 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) } - 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", Name: "test", @@ -189,7 +189,7 @@ func TestListContainers(t *testing.T) { Labels: map[string]string{ "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), StartedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), FinishedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), diff --git a/internal/agent/server.go b/internal/agent/server.go index bfaaefaf..905ea418 100644 --- a/internal/agent/server.go +++ b/internal/agent/server.go @@ -12,7 +12,7 @@ import ( "time" "github.com/amir20/dozzle/internal/agent/pb" - "github.com/amir20/dozzle/internal/docker" + "github.com/amir20/dozzle/internal/container" "github.com/rs/zerolog/log" orderedmap "github.com/wk8/go-ordered-map/v2" "google.golang.org/grpc" @@ -25,19 +25,19 @@ import ( ) type server struct { - client docker.Client - store *docker.ContainerStore + client container.Client + store *container.ContainerStore version string 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{ client: client, 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() } - container, err := s.store.FindContainer(in.ContainerId, docker.ContainerFilter{}) + c, err := s.store.FindContainer(in.ContainerId, container.ContainerFilter{}) if err != nil { 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 { return err } - g := docker.NewEventGenerator(out.Context(), reader, container) + g := container.NewEventGenerator(out.Context(), reader, c) for event := range g.Events { 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 { - 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 { return err } - container, err := s.client.FindContainer(out.Context(), in.ContainerId) + c, err := s.client.FindContainer(out.Context(), in.ContainerId) if err != nil { return err } - g := docker.NewEventGenerator(out.Context(), reader, container) + g := container.NewEventGenerator(out.Context(), reader, c) for { 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 { - 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 { 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 { - events := make(chan docker.ContainerEvent) + events := make(chan container.ContainerEvent) 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 { - stats := make(chan docker.ContainerStat) + stats := make(chan container.ContainerStat) 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) { - filter := make(docker.ContainerFilter) + filter := make(container.ContainerFilter) if in.GetFilter() != nil { for k, v := range in.GetFilter() { 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) { - filter := make(docker.ContainerFilter) + filter := make(container.ContainerFilter) if in.GetFilter() != nil { for k, v := range in.GetFilter() { 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 { - containers := make(chan docker.Container) + containers := make(chan container.Container) 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) { - var action docker.ContainerAction + var action container.ContainerAction switch in.Action { case pb.ContainerAction_Start: - action = docker.Start + action = container.Start case pb.ContainerAction_Stop: - action = docker.Stop + action = container.Stop case pb.ContainerAction_Restart: - action = docker.Restart + action = container.Restart default: 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 } -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() c, err := x509.ParseCertificate(certificates.Certificate[0]) if err != nil { @@ -346,7 +346,7 @@ func NewServer(client docker.Client, certificates tls.Certificate, dozzleVersion return grpcServer, nil } -func logEventToPb(event *docker.LogEvent) *pb.LogEvent { +func logEventToPb(event *container.LogEvent) *pb.LogEvent { var message *anypb.Any if event.Message == nil { diff --git a/internal/auth/proxy.go b/internal/auth/proxy.go index 2769d444..6cc3c655 100644 --- a/internal/auth/proxy.go +++ b/internal/auth/proxy.go @@ -7,7 +7,7 @@ import ( "net/http" "strings" - "github.com/amir20/dozzle/internal/docker" + "github.com/amir20/dozzle/internal/container" "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 { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 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 { log.Fatal().Str("filter", r.Header.Get(p.headerFilter)).Msg("Failed to parse container filter") } diff --git a/internal/auth/users.go b/internal/auth/users.go index 77bf79f2..b6e28fe4 100644 --- a/internal/auth/users.go +++ b/internal/auth/users.go @@ -11,7 +11,7 @@ import ( "os" "time" - "github.com/amir20/dozzle/internal/docker" + "github.com/amir20/dozzle/internal/container" "github.com/go-chi/jwtauth/v5" "github.com/rs/zerolog/log" "golang.org/x/crypto/bcrypt" @@ -19,12 +19,12 @@ import ( ) type User struct { - Username string `json:"username" yaml:"-"` - Email string `json:"email" yaml:"email"` - Name string `json:"name" yaml:"name"` - Password string `json:"-" yaml:"password"` - Filter string `json:"-" yaml:"filter"` - ContainerFilter docker.ContainerFilter `json:"-" yaml:"-"` + Username string `json:"username" yaml:"-"` + Email string `json:"email" yaml:"email"` + Name string `json:"name" yaml:"name"` + Password string `json:"-" yaml:"password"` + Filter string `json:"-" yaml:"filter"` + ContainerFilter container.ContainerFilter `json:"-" yaml:"-"` } 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)) } -func newUser(username, email, name string, filter docker.ContainerFilter) User { +func newUser(username, email, name string, filter container.ContainerFilter) User { return User{ Username: username, Email: email, @@ -197,9 +197,9 @@ func UserFromContext(ctx context.Context) *User { } email := claims["email"].(string) name := claims["name"].(string) - containerFilter := docker.ContainerFilter{} + containerFilter := container.ContainerFilter{} if filter, ok := claims["filter"].(string); ok { - containerFilter, err = docker.ParseContainerFilter(filter) + containerFilter, err = container.ParseContainerFilter(filter) if err != nil { log.Fatal().Err(err).Str("filter", filter).Msg("Failed to parse container filter") } diff --git a/internal/container/client.go b/internal/container/client.go new file mode 100644 index 00000000..b42f1cd7 --- /dev/null +++ b/internal/container/client.go @@ -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 +} diff --git a/internal/docker/container_store.go b/internal/container/container_store.go similarity index 99% rename from internal/docker/container_store.go rename to internal/container/container_store.go index b9975dd3..baca0df0 100644 --- a/internal/docker/container_store.go +++ b/internal/container/container_store.go @@ -1,4 +1,4 @@ -package docker +package container import ( "context" diff --git a/internal/docker/container_store_test.go b/internal/container/container_store_test.go similarity index 93% rename from internal/docker/container_store_test.go rename to internal/container/container_store_test.go index c2a9764e..f1614e2c 100644 --- a/internal/docker/container_store_test.go +++ b/internal/container/container_store_test.go @@ -1,4 +1,4 @@ -package docker +package container import ( "context" @@ -48,7 +48,7 @@ func TestContainerStore_List(t *testing.T) { Name: "test", }, }, 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.Done() }) @@ -83,7 +83,7 @@ func TestContainerStore_die(t *testing.T) { }, }, 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) { ctx := args.Get(0).(context.Context) events := args.Get(1).(chan<- ContainerEvent) @@ -98,7 +98,7 @@ func TestContainerStore_die(t *testing.T) { 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{ ID: "1234", diff --git a/internal/docker/escape.go b/internal/container/escape.go similarity index 98% rename from internal/docker/escape.go rename to internal/container/escape.go index 27815666..9ddc0e68 100644 --- a/internal/docker/escape.go +++ b/internal/container/escape.go @@ -1,4 +1,4 @@ -package docker +package container import ( "html" @@ -30,7 +30,6 @@ func escape(logEvent *LogEvent) { } func escapeAnyMap(orderedMap *orderedmap.OrderedMap[string, any]) { - for pair := orderedMap.Oldest(); pair != nil; pair = pair.Next() { switch value := pair.Value.(type) { case string: diff --git a/internal/docker/event_generator.go b/internal/container/event_generator.go similarity index 99% rename from internal/docker/event_generator.go rename to internal/container/event_generator.go index 7c19bd70..daa603dc 100644 --- a/internal/docker/event_generator.go +++ b/internal/container/event_generator.go @@ -1,4 +1,4 @@ -package docker +package container import ( "bufio" diff --git a/internal/docker/event_generator_test.go b/internal/container/event_generator_test.go similarity index 99% rename from internal/docker/event_generator_test.go rename to internal/container/event_generator_test.go index 6eba1b5e..05a0a46c 100644 --- a/internal/docker/event_generator_test.go +++ b/internal/container/event_generator_test.go @@ -1,4 +1,4 @@ -package docker +package container import ( "bufio" diff --git a/internal/docker/host.go b/internal/container/host.go similarity index 97% rename from internal/docker/host.go rename to internal/container/host.go index 60e4ae4e..49806d5f 100644 --- a/internal/docker/host.go +++ b/internal/container/host.go @@ -1,4 +1,4 @@ -package docker +package container import ( "fmt" @@ -25,6 +25,7 @@ type Host struct { AgentVersion string `json:"agentVersion,omitempty"` Type string `json:"type"` Available bool `json:"available"` + Swarm bool `json:"-"` } func (h Host) String() string { diff --git a/internal/docker/level_guesser.go b/internal/container/level_guesser.go similarity index 99% rename from internal/docker/level_guesser.go rename to internal/container/level_guesser.go index 32097ec8..91079c3e 100644 --- a/internal/docker/level_guesser.go +++ b/internal/container/level_guesser.go @@ -1,4 +1,4 @@ -package docker +package container import ( "regexp" diff --git a/internal/docker/level_guesser_test.go b/internal/container/level_guesser_test.go similarity index 99% rename from internal/docker/level_guesser_test.go rename to internal/container/level_guesser_test.go index 89491540..70ff08eb 100644 --- a/internal/docker/level_guesser_test.go +++ b/internal/container/level_guesser_test.go @@ -1,4 +1,4 @@ -package docker +package container import ( "encoding/json" diff --git a/internal/docker/logfmt.go b/internal/container/logfmt.go similarity index 98% rename from internal/docker/logfmt.go rename to internal/container/logfmt.go index f8652dcc..de155131 100644 --- a/internal/docker/logfmt.go +++ b/internal/container/logfmt.go @@ -1,4 +1,4 @@ -package docker +package container import ( "errors" diff --git a/internal/docker/logfmt_test.go b/internal/container/logfmt_test.go similarity index 99% rename from internal/docker/logfmt_test.go rename to internal/container/logfmt_test.go index b12c2961..4e4003e9 100644 --- a/internal/docker/logfmt_test.go +++ b/internal/container/logfmt_test.go @@ -1,4 +1,4 @@ -package docker +package container import ( "encoding/json" diff --git a/internal/docker/stats_collector.go b/internal/container/stats_collector.go similarity index 99% rename from internal/docker/stats_collector.go rename to internal/container/stats_collector.go index e6d382c3..d3e48a9d 100644 --- a/internal/docker/stats_collector.go +++ b/internal/container/stats_collector.go @@ -1,4 +1,4 @@ -package docker +package container import ( "context" diff --git a/internal/docker/stats_collector_test.go b/internal/container/stats_collector_test.go similarity index 95% rename from internal/docker/stats_collector_test.go rename to internal/container/stats_collector_test.go index 47a8ab1d..f9fcfe3d 100644 --- a/internal/docker/stats_collector_test.go +++ b/internal/container/stats_collector_test.go @@ -1,4 +1,4 @@ -package docker +package container import ( "context" @@ -17,13 +17,13 @@ func startedCollector(ctx context.Context) *StatsCollector { State: "running", }, }, nil) - client.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")). + client.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- container.ContainerEvent")). Return(nil). Run(func(args mock.Arguments) { ctx := args.Get(0).(context.Context) <-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). Run(func(args mock.Arguments) { stats := args.Get(2).(chan<- ContainerStat) diff --git a/internal/docker/stripansi.go b/internal/container/stripansi.go similarity index 93% rename from internal/docker/stripansi.go rename to internal/container/stripansi.go index 6cfa2448..1ece3ab8 100644 --- a/internal/docker/stripansi.go +++ b/internal/container/stripansi.go @@ -1,4 +1,4 @@ -package docker +package container import ( "regexp" diff --git a/internal/docker/types.go b/internal/container/types.go similarity index 92% rename from internal/docker/types.go rename to internal/container/types.go index af1e6a42..af2c1810 100644 --- a/internal/docker/types.go +++ b/internal/container/types.go @@ -1,4 +1,4 @@ -package docker +package container import ( "fmt" @@ -7,7 +7,6 @@ import ( "time" "github.com/amir20/dozzle/internal/utils" - "github.com/docker/docker/api/types/filters" ) // Container represents an internal representation of docker containers @@ -70,17 +69,6 @@ func (f ContainerFilter) Exists() bool { 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 const ( diff --git a/internal/docker/client.go b/internal/docker/client.go index 551f627f..b80d3394 100644 --- a/internal/docker/client.go +++ b/internal/docker/client.go @@ -11,10 +11,12 @@ import ( "encoding/json" + "github.com/amir20/dozzle/internal/container" "github.com/amir20/dozzle/internal/utils" "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/filters" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/system" "github.com/docker/docker/client" @@ -22,70 +24,41 @@ import ( "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 { - ContainerList(context.Context, container.ListOptions) ([]types.Container, error) - ContainerLogs(context.Context, string, container.LogsOptions) (io.ReadCloser, error) + ContainerList(context.Context, docker.ListOptions) ([]types.Container, error) + ContainerLogs(context.Context, string, docker.LogsOptions) (io.ReadCloser, error) Events(context.Context, events.ListOptions) (<-chan events.Message, <-chan 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) - ContainerStart(ctx context.Context, containerID string, options container.StartOptions) error - ContainerStop(ctx context.Context, containerID string, options container.StopOptions) error - ContainerRestart(ctx context.Context, containerID string, options container.StopOptions) error + ContainerStart(ctx context.Context, containerID string, options docker.StartOptions) error + ContainerStop(ctx context.Context, containerID string, options docker.StopOptions) error + ContainerRestart(ctx context.Context, containerID string, options docker.StopOptions) 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 { cli DockerCLI - host Host + host container.Host 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()) if err != nil { 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.MemTotal = info.MemTotal host.DockerVersion = info.ServerVersion + host.Swarm = info.Swarm.NodeID != "" return &httpClient{ cli: cli, @@ -95,7 +68,7 @@ func NewClient(cli DockerCLI, host Host) Client { } // 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()) if err != nil { @@ -107,16 +80,8 @@ func NewLocalClient(hostname string) (Client, error) { return nil, err } - id := info.ID - if info.Swarm.NodeID != "" { - id = info.Swarm.NodeID - } - - host := Host{ - ID: id, + host := container.Host{ Name: info.Name, - MemTotal: info.MemTotal, - NCPU: info.NCPU, Endpoint: "local", Type: "local", } @@ -128,7 +93,7 @@ func NewLocalClient(hostname string) (Client, error) { return NewClient(cli, host), nil } -func NewRemoteClient(host Host) (Client, error) { +func NewRemoteClient(host container.Host) (container.Client, error) { if host.URL.Scheme != "tcp" { 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 -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") if json, err := d.cli.ContainerInspect(ctx, id); err == nil { return newContainerFromJSON(json, d.host.ID), nil } 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 { - case Start: - return d.cli.ContainerStart(ctx, containerID, container.StartOptions{}) - case Stop: - return d.cli.ContainerStop(ctx, containerID, container.StopOptions{}) - case Restart: - return d.cli.ContainerRestart(ctx, containerID, container.StopOptions{}) + case container.Start: + return d.cli.ContainerStart(ctx, containerID, docker.StartOptions{}) + case container.Stop: + return d.cli.ContainerStop(ctx, containerID, docker.StopOptions{}) + case container.Restart: + return d.cli.ContainerRestart(ctx, containerID, docker.StopOptions{}) default: 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") - containerListOptions := container.ListOptions{ - Filters: filter.asArgs(), + filterArgs := filters.NewArgs() + for key, values := range filter { + for _, value := range values { + filterArgs.Add(key, value) + } + } + containerListOptions := docker.ListOptions{ + Filters: filterArgs, All: true, } list, err := d.cli.ContainerList(ctx, containerListOptions) @@ -192,7 +163,7 @@ func (d *httpClient) ListContainers(ctx context.Context, filter ContainerFilter) return nil, err } - var containers = make([]Container, 0, len(list)) + var containers = make([]container.Container, 0, len(list)) for _, c := range list { containers = append(containers, newContainer(c, d.host.ID)) } @@ -204,7 +175,7 @@ func (d *httpClient) ListContainers(ctx context.Context, filter ContainerFilter) 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) if err != nil { @@ -213,7 +184,7 @@ func (d *httpClient) ContainerStats(ctx context.Context, id string, stats chan<- defer response.Body.Close() decoder := json.NewDecoder(response.Body) - var v *container.StatsResponse + var v *docker.StatsResponse for { if err := decoder.Decode(&v); err != nil { return err @@ -243,7 +214,7 @@ func (d *httpClient) ContainerStats(ctx context.Context, id string, stats chan<- select { case <-ctx.Done(): return nil - case stats <- ContainerStat{ + case stats <- container.ContainerStat{ ID: id, CPUPercent: cpuPercent, 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") sinceQuery := since.Add(-50 * time.Millisecond).Format(time.RFC3339Nano) - options := container.LogsOptions{ - ShowStdout: stdType&STDOUT != 0, - ShowStderr: stdType&STDERR != 0, + options := docker.LogsOptions{ + ShowStdout: stdType&container.STDOUT != 0, + ShowStderr: stdType&container.STDERR != 0, Follow: true, Tail: strconv.Itoa(100), Timestamps: true, @@ -275,7 +246,7 @@ func (d *httpClient) ContainerLogs(ctx context.Context, id string, since time.Ti 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{}) for { @@ -287,7 +258,7 @@ func (d *httpClient) ContainerEvents(ctx context.Context, messages chan<- Contai case message := <-dockerMessages: if message.Type == events.ContainerEventType && len(message.Actor.ID) > 0 { - messages <- ContainerEvent{ + messages <- container.ContainerEvent{ ActorID: message.Actor.ID[:12], Name: string(message.Action), 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") - options := container.LogsOptions{ - ShowStdout: stdType&STDOUT != 0, - ShowStderr: stdType&STDERR != 0, + options := docker.LogsOptions{ + ShowStdout: stdType&container.STDOUT != 0, + ShowStderr: stdType&container.STDERR != 0, Timestamps: true, Since: from.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 } -func (d *httpClient) Ping(ctx context.Context) (types.Ping, error) { - return d.cli.Ping(ctx) +func (d *httpClient) Ping(ctx context.Context) error { + _, 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") return d.host } @@ -330,11 +302,7 @@ func (d *httpClient) IsSwarmMode() bool { return d.info.Swarm.LocalNodeState != swarm.LocalNodeStateInactive } -func (d *httpClient) SystemInfo() system.Info { - return d.info -} - -func newContainer(c types.Container, host string) Container { +func newContainer(c types.Container, host string) container.Container { name := "no name" if 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"] != "" { group = c.Labels["dev.dozzle.group"] } - return Container{ + return container.Container{ ID: c.ID[:12], Name: name, Image: c.Image, @@ -355,12 +323,12 @@ func newContainer(c types.Container, host string) Container { State: c.State, Host: host, Labels: c.Labels, - Stats: utils.NewRingBuffer[ContainerStat](300), // 300 seconds of stats + Stats: utils.NewRingBuffer[container.ContainerStat](300), // 300 seconds of stats Group: group, } } -func newContainerFromJSON(c types.ContainerJSON, host string) Container { +func newContainerFromJSON(c types.ContainerJSON, host string) container.Container { name := "no name" if 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"] } - container := Container{ + container := container.Container{ ID: c.ID[:12], Name: name, Image: c.Config.Image, @@ -381,7 +349,7 @@ func newContainerFromJSON(c types.ContainerJSON, host string) Container { State: c.State.Status, Host: host, 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, Tty: c.Config.Tty, } diff --git a/internal/docker/client_test.go b/internal/docker/client_test.go index 2b478822..dc6a892c 100644 --- a/internal/docker/client_test.go +++ b/internal/docker/client_test.go @@ -10,8 +10,9 @@ import ( "testing" + "github.com/amir20/dozzle/internal/container" "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/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -23,7 +24,7 @@ type mockedProxy struct { 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() containers, ok := args.Get(0).([]types.Container) 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) reader, ok := args.Get(0).(io.ReadCloser) 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) } -func (m *mockedProxy) ContainerStats(ctx context.Context, containerID string, stream bool) (container.StatsResponseReader, error) { - return container.StatsResponseReader{}, nil +func (m *mockedProxy) ContainerStats(ctx context.Context, containerID string, stream bool) (docker.StatsResponseReader, error) { + 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) err := args.Get(0) @@ -63,7 +64,7 @@ func (m *mockedProxy) ContainerStart(ctx context.Context, containerID string, op 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) err := args.Get(0) @@ -74,7 +75,7 @@ func (m *mockedProxy) ContainerStop(ctx context.Context, containerID string, opt 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) 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) { proxy := new(mockedProxy) 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") 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) { proxy := new(mockedProxy) 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") require.Error(t, err, "test.") @@ -124,9 +125,9 @@ func Test_dockerClient_ListContainers_happy(t *testing.T) { proxy := new(mockedProxy) 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.") Ids := []string{"1234567890_a", "abcdefghijkl"} @@ -149,7 +150,7 @@ func Test_dockerClient_ContainerLogs_happy(t *testing.T) { reader := io.NopCloser(bytes.NewReader(b)) since := time.Date(2021, time.January, 1, 0, 0, 0, 0, time.UTC) - options := container.LogsOptions{ + options := docker.LogsOptions{ ShowStdout: true, ShowStderr: true, Follow: true, @@ -158,8 +159,8 @@ func Test_dockerClient_ContainerLogs_happy(t *testing.T) { Since: "2020-12-31T23:59:59.95Z"} proxy.On("ContainerLogs", mock.Anything, id, options).Return(reader, nil) - client := &httpClient{proxy, Host{ID: "localhost"}, system.Info{}} - logReader, _ := client.ContainerLogs(context.Background(), id, since, STDALL) + client := &httpClient{proxy, container.Host{ID: "localhost"}, system.Info{}} + logReader, _ := client.ContainerLogs(context.Background(), id, since, container.STDALL) actual, _ := io.ReadAll(logReader) 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")) - 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.Error(t, err, "error should have been returned") @@ -185,10 +186,10 @@ func Test_dockerClient_FindContainer_happy(t *testing.T) { proxy := new(mockedProxy) 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) - client := &httpClient{proxy, Host{ID: "localhost"}, system.Info{}} + client := &httpClient{proxy, container.Host{ID: "localhost"}, system.Info{}} container, err := client.FindContainer(context.Background(), "abcdefghijkl") 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) { proxy := new(mockedProxy) 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") 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) { 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)} - 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("ContainerStart", 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) - container, err := client.FindContainer(context.Background(), "abcdefghijkl") + c, err := client.FindContainer(context.Background(), "abcdefghijkl") 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"} 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") 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) { 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("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("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") actions := []string{"start", "stop", "restart"} 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") assert.Error(t, err, "error should have been returned") } diff --git a/internal/healthcheck/rpc.go b/internal/healthcheck/rpc.go index 3fe7b244..e96f8023 100644 --- a/internal/healthcheck/rpc.go +++ b/internal/healthcheck/rpc.go @@ -5,7 +5,7 @@ import ( "crypto/tls" "github.com/amir20/dozzle/internal/agent" - "github.com/amir20/dozzle/internal/docker" + "github.com/amir20/dozzle/internal/container" "github.com/rs/zerolog/log" ) @@ -14,7 +14,7 @@ func RPCRequest(ctx context.Context, addr string, certs tls.Certificate) error { if err != nil { 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") return err } diff --git a/internal/support/cli/analytics.go b/internal/support/cli/analytics.go index 47013c69..d8eafcf6 100644 --- a/internal/support/cli/analytics.go +++ b/internal/support/cli/analytics.go @@ -2,12 +2,12 @@ package cli import ( "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/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 { return } @@ -29,7 +29,7 @@ func StartEvent(args Args, mode string, client docker.Client, subCommand string) host := client.Host() event.ServerID = host.ID event.ServerVersion = host.DockerVersion - event.IsSwarmMode = client.SystemInfo().Swarm.NodeID != "" + event.IsSwarmMode = host.Swarm } else { event.ServerID = "n/a" } diff --git a/internal/support/cli/clients.go b/internal/support/cli/clients.go index 94071964..69880c0c 100644 --- a/internal/support/cli/clients.go +++ b/internal/support/cli/clients.go @@ -4,19 +4,20 @@ import ( "context" "embed" + "github.com/amir20/dozzle/internal/container" "github.com/amir20/dozzle/internal/docker" docker_support "github.com/amir20/dozzle/internal/support/docker" "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 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`) } for _, remoteHost := range args.RemoteHost { - host, err := docker.ParseConnection(remoteHost) + host, err := container.ParseConnection(remoteHost) if err != nil { log.Fatal().Err(err).Interface("host", remoteHost).Msg("Could not parse remote host") } diff --git a/internal/support/docker/agent_service.go b/internal/support/docker/agent_service.go index 9f095f8b..33d9e1d1 100644 --- a/internal/support/docker/agent_service.go +++ b/internal/support/docker/agent_service.go @@ -7,13 +7,13 @@ import ( "time" "github.com/amir20/dozzle/internal/agent" - "github.com/amir20/dozzle/internal/docker" + "github.com/amir20/dozzle/internal/container" "github.com/rs/zerolog/log" ) type agentService struct { client *agent.Client - host docker.Host + host container.Host } 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) } -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) } -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) } -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) } -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") 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) if err != nil { host := a.host @@ -55,18 +55,18 @@ func (a *agentService) Host(ctx context.Context) (docker.Host, error) { 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) } -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) } -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) } -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) } diff --git a/internal/support/docker/client_service.go b/internal/support/docker/client_service.go index 01c5a315..2d3c2769 100644 --- a/internal/support/docker/client_service.go +++ b/internal/support/docker/client_service.go @@ -5,59 +5,59 @@ import ( "io" "time" - "github.com/amir20/dozzle/internal/docker" + "github.com/amir20/dozzle/internal/container" ) type ClientService interface { - FindContainer(ctx context.Context, id string, filter docker.ContainerFilter) (docker.Container, error) - ListContainers(ctx context.Context, filter docker.ContainerFilter) ([]docker.Container, error) - Host(ctx context.Context) (docker.Host, error) - ContainerAction(ctx context.Context, container docker.Container, action docker.ContainerAction) error - LogsBetweenDates(ctx context.Context, container docker.Container, from time.Time, to time.Time, stdTypes docker.StdType) (<-chan *docker.LogEvent, error) - RawLogs(ctx context.Context, container docker.Container, from time.Time, to time.Time, stdTypes docker.StdType) (io.ReadCloser, error) + FindContainer(ctx context.Context, id string, filter container.ContainerFilter) (container.Container, error) + ListContainers(ctx context.Context, filter container.ContainerFilter) ([]container.Container, error) + Host(ctx context.Context) (container.Host, error) + ContainerAction(ctx context.Context, container container.Container, action container.ContainerAction) 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 container.Container, from time.Time, to time.Time, stdTypes container.StdType) (io.ReadCloser, error) // Subscriptions - SubscribeStats(ctx context.Context, stats chan<- docker.ContainerStat) - SubscribeEvents(ctx context.Context, events chan<- docker.ContainerEvent) - SubscribeContainersStarted(ctx context.Context, containers chan<- docker.Container) + SubscribeStats(ctx context.Context, stats chan<- container.ContainerStat) + SubscribeEvents(ctx context.Context, events chan<- container.ContainerEvent) + SubscribeContainersStarted(ctx context.Context, containers chan<- container.Container) // 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 { - client docker.Client - store *docker.ContainerStore + client container.Client + store *container.ContainerStore } -func NewDockerClientService(client docker.Client, filter docker.ContainerFilter) ClientService { +func NewDockerClientService(client container.Client, filter container.ContainerFilter) ClientService { return &dockerClientService{ 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) } -func (d *dockerClientService) LogsBetweenDates(ctx context.Context, container docker.Container, from time.Time, to time.Time, stdTypes docker.StdType) (<-chan *docker.LogEvent, error) { - reader, err := d.client.ContainerLogsBetweenDates(ctx, container.ID, from, to, stdTypes) +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, c.ID, from, to, stdTypes) if err != nil { return nil, err } - g := docker.NewEventGenerator(ctx, reader, container) + g := container.NewEventGenerator(ctx, reader, c) 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 { - reader, err := d.client.ContainerLogs(ctx, container.ID, from, stdTypes) +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, c.ID, from, stdTypes) if err != nil { return err } - g := docker.NewEventGenerator(ctx, reader, container) + g := container.NewEventGenerator(ctx, reader, c) for event := range g.Events { 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) } -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) } -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) } -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 } -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) } -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) } -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) } diff --git a/internal/support/docker/container_service.go b/internal/support/docker/container_service.go index de1ca4d6..b94b1d94 100644 --- a/internal/support/docker/container_service.go +++ b/internal/support/docker/container_service.go @@ -5,26 +5,26 @@ import ( "io" "time" - "github.com/amir20/dozzle/internal/docker" + "github.com/amir20/dozzle/internal/container" ) type containerService struct { 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) } -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) } -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) } -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) } diff --git a/internal/support/docker/multi_host_service.go b/internal/support/docker/multi_host_service.go index 9cdf584c..a5b7d767 100644 --- a/internal/support/docker/multi_host_service.go +++ b/internal/support/docker/multi_host_service.go @@ -5,14 +5,14 @@ import ( "fmt" "time" - "github.com/amir20/dozzle/internal/docker" + "github.com/amir20/dozzle/internal/container" "github.com/rs/zerolog/log" ) -type ContainerFilter = func(*docker.Container) bool +type ContainerFilter = func(*container.Container) bool type HostUnavailableError struct { - Host docker.Host + Host container.Host Err error } @@ -24,9 +24,9 @@ type ClientManager interface { Find(id string) (ClientService, bool) List() []ClientService RetryAndList() ([]ClientService, []error) - Subscribe(ctx context.Context, channel chan<- docker.Host) - Hosts(ctx context.Context) []docker.Host - LocalClients() []docker.Client + Subscribe(ctx context.Context, channel chan<- container.Host) + Hosts(ctx context.Context) []container.Host + LocalClients() []container.Client } type MultiHostService struct { @@ -43,7 +43,7 @@ func NewMultiHostService(manager ClientManager, timeout time.Duration) *MultiHos 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) if !ok { 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 } -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) if !ok { 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) } -func (m *MultiHostService) ListAllContainers(filter docker.ContainerFilter) ([]docker.Container, []error) { - containers := make([]docker.Container, 0) +func (m *MultiHostService) ListAllContainers(filter container.ContainerFilter) ([]container.Container, []error) { + containers := make([]container.Container, 0) clients, errors := m.manager.RetryAndList() for _, client := range clients { @@ -94,9 +94,9 @@ func (m *MultiHostService) ListAllContainers(filter docker.ContainerFilter) ([]d 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) - filtered := make([]docker.Container, 0, len(containers)) + filtered := make([]container.Container, 0, len(containers)) for _, container := range containers { if filter(&container) { filtered = append(filtered, container) @@ -105,15 +105,15 @@ func (m *MultiHostService) ListAllContainersFiltered(userFilter docker.Container 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() { client.SubscribeEvents(ctx, events) client.SubscribeStats(ctx, stats) } } -func (m *MultiHostService) SubscribeContainersStarted(ctx context.Context, containers chan<- docker.Container, filter ContainerFilter) { - newContainers := make(chan docker.Container) +func (m *MultiHostService) SubscribeContainersStarted(ctx context.Context, containers chan<- container.Container, filter ContainerFilter) { + newContainers := make(chan container.Container) for _, client := range m.manager.List() { client.SubscribeContainersStarted(ctx, newContainers) } @@ -139,25 +139,25 @@ func (m *MultiHostService) TotalClients() int { 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) defer cancel() return m.manager.Hosts(ctx) } -func (m *MultiHostService) LocalHost() (docker.Host, error) { +func (m *MultiHostService) LocalHost() (container.Host, error) { for _, host := range m.Hosts() { if host.Type == "local" { 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) } -func (m *MultiHostService) LocalClients() []docker.Client { +func (m *MultiHostService) LocalClients() []container.Client { return m.manager.LocalClients() } diff --git a/internal/support/docker/retriable_client_manager.go b/internal/support/docker/retriable_client_manager.go index 35ed023a..4a120ece 100644 --- a/internal/support/docker/retriable_client_manager.go +++ b/internal/support/docker/retriable_client_manager.go @@ -8,7 +8,7 @@ import ( "time" "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/samber/lo" lop "github.com/samber/lo/parallel" @@ -21,7 +21,7 @@ type RetriableClientManager struct { failedAgents []string certs tls.Certificate mu sync.RWMutex - subscribers *xsync.MapOf[context.Context, chan<- docker.Host] + subscribers *xsync.MapOf[context.Context, chan<- container.Host] timeout time.Duration } @@ -72,12 +72,12 @@ func NewRetriableClientManager(agents []string, timeout time.Duration, certs tls clients: clientMap, failedAgents: failed, certs: certs, - subscribers: xsync.NewMapOf[context.Context, chan<- docker.Host](), + subscribers: xsync.NewMapOf[context.Context, chan<- container.Host](), 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) go func() { @@ -111,11 +111,11 @@ func (m *RetriableClientManager) RetryAndList() ([]ClientService, []error) { } 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 // We don't want to block the subscribers in event.go - go func(host docker.Host) { + go func(host container.Host) { select { case channel <- host: 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)) } -func (m *RetriableClientManager) Hosts(ctx context.Context) []docker.Host { +func (m *RetriableClientManager) Hosts(ctx context.Context) []container.Host { 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) if err != nil { 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 { - hosts = append(hosts, docker.Host{ + hosts = append(hosts, container.Host{ ID: endpoint, Name: endpoint, Endpoint: endpoint, @@ -180,10 +180,10 @@ func (m *RetriableClientManager) Hosts(ctx context.Context) []docker.Host { return hosts } -func (m *RetriableClientManager) LocalClients() []docker.Client { +func (m *RetriableClientManager) LocalClients() []container.Client { services := m.List() - clients := make([]docker.Client, 0) + clients := make([]container.Client, 0) for _, service := range services { if clientService, ok := service.(*dockerClientService); ok { diff --git a/internal/support/docker/swarm_client_manager.go b/internal/support/docker/swarm_client_manager.go index c07070d9..462998c4 100644 --- a/internal/support/docker/swarm_client_manager.go +++ b/internal/support/docker/swarm_client_manager.go @@ -10,7 +10,7 @@ import ( "time" "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/samber/lo" lop "github.com/samber/lo/parallel" @@ -22,8 +22,8 @@ type SwarmClientManager struct { clients map[string]ClientService certs tls.Certificate mu sync.RWMutex - subscribers *xsync.MapOf[context.Context, chan<- docker.Host] - localClient docker.Client + subscribers *xsync.MapOf[context.Context, chan<- container.Host] + localClient container.Client localIPs []string name string timeout time.Duration @@ -47,7 +47,7 @@ func localIPs() []string { 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) localService := NewDockerClientService(localClient, filter) 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) defer cancel() - container, err := localClient.FindContainer(ctx, id) + c, err := localClient.FindContainer(ctx, id) if err != nil { 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") @@ -72,7 +72,7 @@ func NewSwarmClientManager(localClient docker.Client, certs tls.Certificate, tim localClient: localClient, clients: clientMap, certs: certs, - subscribers: xsync.NewMapOf[context.Context, chan<- docker.Host](), + subscribers: xsync.NewMapOf[context.Context, chan<- container.Host](), localIPs: localIPs(), name: serviceName, 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.agentManager.Subscribe(ctx, channel) @@ -159,7 +159,7 @@ func (m *SwarmClientManager) RetryAndList() ([]ClientService, []error) { m.clients[host.ID] = client 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.Type = "swarm" @@ -205,12 +205,12 @@ func (m *SwarmClientManager) Find(id string) (ClientService, bool) { return client, ok } -func (m *SwarmClientManager) Hosts(ctx context.Context) []docker.Host { +func (m *SwarmClientManager) Hosts(ctx context.Context) []container.Host { m.mu.RLock() clients := lo.Values(m.clients) 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) if err != nil { 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)) } -func (m *SwarmClientManager) LocalClients() []docker.Client { - return []docker.Client{m.localClient} +func (m *SwarmClientManager) LocalClients() []container.Client { + return []container.Client{m.localClient} } diff --git a/internal/support/search/search.go b/internal/support/search/search.go index 1901494e..8d6f9df3 100644 --- a/internal/support/search/search.go +++ b/internal/support/search/search.go @@ -5,7 +5,7 @@ import ( "regexp" "strings" - "github.com/amir20/dozzle/internal/docker" + "github.com/amir20/dozzle/internal/container" "github.com/rs/zerolog/log" orderedmap "github.com/wk8/go-ordered-map/v2" ) @@ -26,7 +26,7 @@ func ParseRegex(search string) (*regexp.Regexp, error) { 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) { case string: if re.MatchString(value) { diff --git a/internal/web/actions.go b/internal/web/actions.go index 55306f59..5b133d73 100644 --- a/internal/web/actions.go +++ b/internal/web/actions.go @@ -6,7 +6,7 @@ import ( "time" "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/rs/zerolog/log" ) @@ -30,7 +30,7 @@ func (h *handler) containerActions(w http.ResponseWriter, r *http.Request) { return } - parsedAction, err := docker.ParseContainerAction(action) + parsedAction, err := container.ParseContainerAction(action) if err != nil { log.Error().Err(err).Msg("error while trying to parse action") http.Error(w, err.Error(), http.StatusBadRequest) diff --git a/internal/web/actions_test.go b/internal/web/actions_test.go index 9f980040..475847a5 100644 --- a/internal/web/actions_test.go +++ b/internal/web/actions_test.go @@ -7,7 +7,7 @@ import ( "testing" - "github.com/amir20/dozzle/internal/docker" + "github.com/amir20/dozzle/internal/container" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -15,17 +15,17 @@ import ( func mockedClient() *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, "456").Return(docker.Container{}, errors.New("container not found")) - mockedClient.On("ContainerActions", mock.Anything, docker.Start, container.ID).Return(nil) - mockedClient.On("ContainerActions", mock.Anything, docker.Stop, container.ID).Return(nil) - mockedClient.On("ContainerActions", mock.Anything, docker.Restart, container.ID).Return(nil) - mockedClient.On("ContainerActions", mock.Anything, docker.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("Host").Return(docker.Host{ID: "localhost"}) - mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]docker.Container{container}, nil) + mockedClient.On("FindContainer", mock.Anything, "123").Return(c, nil) + mockedClient.On("FindContainer", mock.Anything, "456").Return(container.Container{}, errors.New("container not found")) + mockedClient.On("ContainerActions", mock.Anything, container.Start, c.ID).Return(nil) + mockedClient.On("ContainerActions", mock.Anything, container.Stop, c.ID).Return(nil) + mockedClient.On("ContainerActions", mock.Anything, container.Restart, c.ID).Return(nil) + mockedClient.On("ContainerActions", mock.Anything, container.Start, mock.Anything).Return(errors.New("container not found")) + mockedClient.On("ContainerActions", mock.Anything, container.ContainerAction("something-else"), c.ID).Return(errors.New("unknown action")) + mockedClient.On("Host").Return(container.Host{ID: "localhost"}) + mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]container.Container{c}, nil) mockedClient.On("ContainerEvents", mock.Anything, mock.Anything).Return(nil) return mockedClient diff --git a/internal/web/debug.go b/internal/web/debug.go index ebc39610..4ce19bf8 100644 --- a/internal/web/debug.go +++ b/internal/web/debug.go @@ -4,13 +4,13 @@ import ( "encoding/json" "net/http" - "github.com/amir20/dozzle/internal/docker" + "github.com/amir20/dozzle/internal/container" ) func (h *handler) debugStore(w http.ResponseWriter, r *http.Request) { respone := make(map[string]interface{}) respone["hosts"] = h.multiHostService.Hosts() - containers, errors := h.multiHostService.ListAllContainers(docker.ContainerFilter{}) + containers, errors := h.multiHostService.ListAllContainers(container.ContainerFilter{}) respone["containers"] = containers respone["errors"] = errors diff --git a/internal/web/download.go b/internal/web/download.go index b63675d0..4cc89cdc 100644 --- a/internal/web/download.go +++ b/internal/web/download.go @@ -9,7 +9,7 @@ import ( "time" "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/go-chi/chi/v5" ) @@ -32,12 +32,12 @@ func (h *handler) downloadLogs(w http.ResponseWriter, r *http.Request) { now := time.Now() nowFmt := now.Format("2006-01-02T15-04-05") - var stdTypes docker.StdType + var stdTypes container.StdType if r.URL.Query().Has("stdout") { - stdTypes |= docker.STDOUT + stdTypes |= container.STDOUT } if r.URL.Query().Has("stderr") { - stdTypes |= docker.STDERR + stdTypes |= container.STDERR } if stdTypes == 0 { diff --git a/internal/web/download_test.go b/internal/web/download_test.go index eae17f08..60213817 100644 --- a/internal/web/download_test.go +++ b/internal/web/download_test.go @@ -9,7 +9,7 @@ import ( "net/http/httptest" "testing" - "github.com/amir20/dozzle/internal/docker" + "github.com/amir20/dozzle/internal/container" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) @@ -21,17 +21,17 @@ func Test_handler_download_logs(t *testing.T) { 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("ContainerLogsBetweenDates", mock.Anything, id, mock.Anything, mock.Anything, docker.STDOUT).Return(io.NopCloser(bytes.NewReader(data)), nil) - mockedClient.On("Host").Return(docker.Host{ + mockedClient.On("FindContainer", mock.Anything, id).Return(container.Container{ID: id, Tty: false}, nil) + mockedClient.On("ContainerLogsBetweenDates", mock.Anything, id, mock.Anything, mock.Anything, container.STDOUT).Return(io.NopCloser(bytes.NewReader(data)), nil) + mockedClient.On("Host").Return(container.Host{ 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) }) - 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"}, }, nil) diff --git a/internal/web/events.go b/internal/web/events.go index 97d2557c..837c9fa2 100644 --- a/internal/web/events.go +++ b/internal/web/events.go @@ -5,7 +5,7 @@ import ( "github.com/amir20/dozzle/internal/analytics" "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" support_web "github.com/amir20/dozzle/internal/support/web" "github.com/amir20/dozzle/types" @@ -20,9 +20,9 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) { return } - events := make(chan docker.ContainerEvent) - stats := make(chan docker.ContainerStat) - availableHosts := make(chan docker.Host) + events := make(chan container.ContainerEvent) + stats := make(chan container.ContainerStat) + availableHosts := make(chan container.Host) h.multiHostService.SubscribeEventsAndStats(r.Context(), events, stats) h.multiHostService.SubscribeAvailableHosts(r.Context(), availableHosts) diff --git a/internal/web/events_test.go b/internal/web/events_test.go index 3e86717f..37ffabe4 100644 --- a/internal/web/events_test.go +++ b/internal/web/events_test.go @@ -9,7 +9,7 @@ import ( "net/http/httptest" "testing" - "github.com/amir20/dozzle/internal/docker" + "github.com/amir20/dozzle/internal/container" docker_support "github.com/amir20/dozzle/internal/support/docker" "github.com/amir20/dozzle/internal/utils" "github.com/beme/abide" @@ -24,17 +24,17 @@ func Test_handler_streamEvents_happy(t *testing.T) { mockedClient := new(MockedClient) - mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]docker.Container{}, nil) - mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- docker.ContainerEvent")).Return(nil).Run(func(args mock.Arguments) { - messages := args.Get(1).(chan<- docker.ContainerEvent) + mockedClient.On("ListContainers", mock.Anything, mock.Anything).Return([]container.Container{}, nil) + mockedClient.On("ContainerEvents", mock.Anything, mock.AnythingOfType("chan<- container.ContainerEvent")).Return(nil).Run(func(args mock.Arguments) { + messages := args.Get(1).(chan<- container.ContainerEvent) time.Sleep(50 * time.Millisecond) - messages <- docker.ContainerEvent{ + messages <- container.ContainerEvent{ Name: "start", ActorID: "1234", Host: "localhost", } - messages <- docker.ContainerEvent{ + messages <- container.ContainerEvent{ Name: "something-random", ActorID: "1234", Host: "localhost", @@ -42,19 +42,19 @@ func Test_handler_streamEvents_happy(t *testing.T) { time.Sleep(50 * time.Millisecond) cancel() }) - mockedClient.On("FindContainer", mock.Anything, "1234").Return(docker.Container{ + mockedClient.On("FindContainer", mock.Anything, "1234").Return(container.Container{ ID: "1234", Name: "test", Image: "test", - Stats: utils.NewRingBuffer[docker.ContainerStat](300), // 300 seconds of stats + Stats: utils.NewRingBuffer[container.ContainerStat](300), // 300 seconds of stats }, nil) - mockedClient.On("Host").Return(docker.Host{ + mockedClient.On("Host").Return(container.Host{ ID: "localhost", }) // 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) server := CreateServer(multiHostService, nil, Config{Base: "/", Authorization: Authorization{Provider: NONE}}) diff --git a/internal/web/healthcheck.go b/internal/web/healthcheck.go index 339b0646..f542ccc7 100644 --- a/internal/web/healthcheck.go +++ b/internal/web/healthcheck.go @@ -11,7 +11,7 @@ func (h *handler) healthcheck(w http.ResponseWriter, r *http.Request) { clients := h.multiHostService.LocalClients() 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") w.WriteHeader(http.StatusInternalServerError) } diff --git a/internal/web/logs.go b/internal/web/logs.go index 8ad96729..ecd2ffdf 100644 --- a/internal/web/logs.go +++ b/internal/web/logs.go @@ -17,7 +17,7 @@ import ( "time" "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" support_web "github.com/amir20/dozzle/internal/support/web" "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")) id := chi.URLParam(r, "id") - var stdTypes docker.StdType + var stdTypes container.StdType if r.URL.Query().Has("stdout") { - stdTypes |= docker.STDOUT + stdTypes |= container.STDOUT } if r.URL.Query().Has("stderr") { - stdTypes |= docker.STDERR + stdTypes |= container.STDERR } if stdTypes == 0 { @@ -61,7 +61,7 @@ func (h *handler) fetchLogsBetweenDates(w http.ResponseWriter, r *http.Request) return } - buffer := utils.NewRingBuffer[*docker.LogEvent](500) + buffer := utils.NewRingBuffer[*container.LogEvent](500) delta := max(to.Sub(from), time.Second*3) 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) { 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) }) } @@ -192,14 +192,14 @@ func (h *handler) streamLogsMerged(w http.ResponseWriter, r *http.Request) { 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) }) } func (h *handler) streamServiceLogs(w http.ResponseWriter, r *http.Request) { 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 }) } @@ -207,7 +207,7 @@ func (h *handler) streamServiceLogs(w http.ResponseWriter, r *http.Request) { func (h *handler) streamGroupedLogs(w http.ResponseWriter, r *http.Request) { 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 }) } @@ -215,25 +215,25 @@ func (h *handler) streamGroupedLogs(w http.ResponseWriter, r *http.Request) { func (h *handler) streamStackLogs(w http.ResponseWriter, r *http.Request) { 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 }) } func (h *handler) streamHostLogs(w http.ResponseWriter, r *http.Request) { 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 }) } 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") { - stdTypes |= docker.STDOUT + stdTypes |= container.STDOUT } if r.URL.Query().Has("stderr") { - stdTypes |= docker.STDERR + stdTypes |= container.STDERR } if stdTypes == 0 { @@ -263,9 +263,9 @@ func (h *handler) streamLogsForContainers(w http.ResponseWriter, r *http.Request absoluteTime := time.Time{} var regex *regexp.Regexp - liveLogs := make(chan *docker.LogEvent) - events := make(chan *docker.ContainerEvent, 1) - backfill := make(chan []*docker.LogEvent) + liveLogs := make(chan *container.LogEvent) + events := make(chan *container.ContainerEvent, 1) + backfill := make(chan []*container.LogEvent) levels := make(map[string]struct{}) 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 to := absoluteTime for minimum > 0 { - events := make([]*docker.LogEvent, 0) + events := make([]*container.LogEvent, 0) stillRunning := false for _, container := range existingContainers { 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) { - containerService, err := h.multiHostService.FindContainer(container.Host, container.ID, usersFilter) + streamLogs := func(c container.Container) { + containerService, err := h.multiHostService.FindContainer(c.Host, c.ID, usersFilter) if err != nil { log.Error().Err(err).Msg("error while finding container") return } - container = containerService.Container - start := utils.Max(absoluteTime, container.StartedAt) + c = containerService.Container + start := utils.Max(absoluteTime, c.StartedAt) err = containerService.StreamLogs(r.Context(), start, stdTypes, liveLogs) if err != nil { if errors.Is(err, io.EOF) { - log.Debug().Str("container", container.ID).Msg("streaming ended") - finishedAt := container.FinishedAt - if container.FinishedAt.IsZero() { + log.Debug().Str("container", c.ID).Msg("streaming ended") + finishedAt := c.FinishedAt + if c.FinishedAt.IsZero() { finishedAt = time.Now() } - events <- &docker.ContainerEvent{ - ActorID: container.ID, + events <- &container.ContainerEvent{ + ActorID: c.ID, Name: "container-stopped", - Host: container.Host, + Host: c.Host, Time: finishedAt, } } 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) } - newContainers := make(chan docker.Container) + newContainers := make(chan container.Container) h.multiHostService.SubscribeContainersStarted(r.Context(), newContainers, containerFilter) ticker := time.NewTicker(5 * time.Second) @@ -387,10 +387,10 @@ loop: continue } sseWriter.Message(logEvent) - case container := <-newContainers: - if _, err := h.multiHostService.FindContainer(container.Host, container.ID, usersFilter); err == nil { - events <- &docker.ContainerEvent{ActorID: container.ID, Name: "container-started", Host: container.Host} - go streamLogs(container) + case c := <-newContainers: + if _, err := h.multiHostService.FindContainer(c.Host, c.ID, usersFilter); err == nil { + events <- &container.ContainerEvent{ActorID: c.ID, Name: "container-started", Host: c.Host} + go streamLogs(c) } case event := <-events: diff --git a/internal/web/logs_test.go b/internal/web/logs_test.go index 54008c7c..d40c4752 100644 --- a/internal/web/logs_test.go +++ b/internal/web/logs_test.go @@ -14,7 +14,7 @@ import ( "strings" "testing" - "github.com/amir20/dozzle/internal/docker" + "github.com/amir20/dozzle/internal/container" "github.com/beme/abide" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -36,25 +36,25 @@ func Test_handler_streamLogs_happy(t *testing.T) { mockedClient := new(MockedClient) - data := makeMessage("INFO Testing logs...", docker.STDOUT) + data := makeMessage("INFO Testing logs...", container.STDOUT) now := time.Now() - mockedClient.On("FindContainer", mock.Anything, id).Return(docker.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("FindContainer", mock.Anything, id).Return(container.Container{ID: id, Tty: false, Host: "localhost", StartedAt: now}, nil) + mockedClient.On("ContainerLogs", mock.Anything, mock.Anything, now, container.STDALL).Return(io.NopCloser(bytes.NewReader(data)), nil). Run(func(args mock.Arguments) { go func() { time.Sleep(50 * time.Millisecond) cancel() }() }) - mockedClient.On("Host").Return(docker.Host{ + mockedClient.On("Host").Return(container.Host{ 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"}, }, 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) }) @@ -80,27 +80,27 @@ func Test_handler_streamLogs_happy_with_id(t *testing.T) { 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) - mockedClient.On("FindContainer", mock.Anything, id).Return(docker.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("FindContainer", mock.Anything, id).Return(container.Container{ID: id, Host: "localhost", StartedAt: started}, nil) + mockedClient.On("ContainerLogs", mock.Anything, mock.Anything, started, container.STDALL).Return(io.NopCloser(bytes.NewReader(data)), nil). Run(func(args mock.Arguments) { go func() { time.Sleep(50 * time.Millisecond) cancel() }() }) - mockedClient.On("Host").Return(docker.Host{ + mockedClient.On("Host").Return(container.Host{ 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"}, }, 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) }) @@ -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) mockedClient := new(MockedClient) - mockedClient.On("FindContainer", mock.Anything, id).Return(docker.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("FindContainer", mock.Anything, id).Return(container.Container{ID: id, Host: "localhost", StartedAt: started}, nil) + mockedClient.On("ContainerLogs", mock.Anything, id, started, container.STDALL).Return(io.NopCloser(strings.NewReader("")), io.EOF). Run(func(args mock.Arguments) { go func() { time.Sleep(50 * time.Millisecond) cancel() }() }) - mockedClient.On("Host").Return(docker.Host{ + mockedClient.On("Host").Return(container.Host{ 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"}, }, 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) 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) mockedClient := new(MockedClient) - mockedClient.On("FindContainer", mock.Anything, id).Return(docker.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("FindContainer", mock.Anything, id).Return(container.Container{ID: id, Host: "localhost", StartedAt: started}, nil) + mockedClient.On("ContainerLogs", mock.Anything, id, started, container.STDALL).Return(io.NopCloser(strings.NewReader("")), errors.New("test error")). Run(func(args mock.Arguments) { go func() { time.Sleep(50 * time.Millisecond) cancel() }() }) - mockedClient.On("Host").Return(docker.Host{ + mockedClient.On("Host").Return(container.Host{ 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"}, }, 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) 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.") mockedClient := new(MockedClient) - mockedClient.On("FindContainer", mock.Anything, id).Return(docker.Container{ID: id, Host: "localhost"}, nil) - mockedClient.On("Host").Return(docker.Host{ + mockedClient.On("FindContainer", mock.Anything, id).Return(container.Container{ID: id, Host: "localhost"}, nil) + mockedClient.On("Host").Return(container.Host{ 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"}, }, 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) { time.Sleep(50 * time.Millisecond) }) @@ -231,19 +231,19 @@ func Test_handler_between_dates(t *testing.T) { mockedClient := new(MockedClient) - first := makeMessage("2020-05-13T18:55:37.772853839Z INFO Testing stdout logs...\n", docker.STDOUT) - second := makeMessage("2020-05-13T18:56:37.772853839Z INFO Testing stderr logs...\n", docker.STDERR) + 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", container.STDERR) data := append(first, second...) - mockedClient.On("ContainerLogsBetweenDates", mock.Anything, id, from, to, docker.STDALL).Return(io.NopCloser(bytes.NewReader(data)), nil) - mockedClient.On("FindContainer", mock.Anything, id).Return(docker.Container{ID: id}, nil) - mockedClient.On("Host").Return(docker.Host{ + mockedClient.On("ContainerLogsBetweenDates", mock.Anything, id, from, to, container.STDALL).Return(io.NopCloser(bytes.NewReader(data)), nil) + mockedClient.On("FindContainer", mock.Anything, id).Return(container.Container{ID: id}, nil) + mockedClient.On("Host").Return(container.Host{ 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"}, }, 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) rr := httptest.NewRecorder() @@ -273,24 +273,24 @@ func Test_handler_between_dates_with_fill(t *testing.T) { mockedClient := new(MockedClient) - first := makeMessage("2020-05-13T18:55:37.772853839Z INFO Testing stdout logs...\n", docker.STDOUT) - second := makeMessage("2020-05-13T18:56:37.772853839Z INFO Testing stderr logs...\n", docker.STDERR) + 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", container.STDERR) 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). 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). Once() - mockedClient.On("FindContainer", mock.Anything, id).Return(docker.Container{ID: id}, nil) - mockedClient.On("Host").Return(docker.Host{ + mockedClient.On("FindContainer", mock.Anything, id).Return(container.Container{ID: id}, nil) + mockedClient.On("Host").Return(container.Host{ 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"}, }, 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) rr := httptest.NewRecorder() @@ -300,7 +300,7 @@ func Test_handler_between_dates_with_fill(t *testing.T) { mockedClient.AssertExpectations(t) } -func makeMessage(message string, stream docker.StdType) []byte { +func makeMessage(message string, stream container.StdType) []byte { data := make([]byte, 8) binary.BigEndian.PutUint32(data[4:], uint32(len(message))) data[0] = byte(stream / 2) diff --git a/internal/web/routes.go b/internal/web/routes.go index 6e27e46d..fd9f727f 100644 --- a/internal/web/routes.go +++ b/internal/web/routes.go @@ -8,7 +8,7 @@ import ( "strings" "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" "github.com/go-chi/chi/v5" @@ -34,7 +34,7 @@ type Config struct { Dev bool Authorization Authorization EnableActions bool - Filter docker.ContainerFilter + Filter container.ContainerFilter } type Authorization struct { diff --git a/internal/web/routes_test.go b/internal/web/routes_test.go index bb23e31c..773a6d21 100644 --- a/internal/web/routes_test.go +++ b/internal/web/routes_test.go @@ -8,7 +8,7 @@ import ( "io" "io/fs" - "github.com/amir20/dozzle/internal/docker" + "github.com/amir20/dozzle/internal/container" docker_support "github.com/amir20/dozzle/internal/support/docker" "github.com/docker/docker/api/types/system" "github.com/go-chi/chi/v5" @@ -20,46 +20,46 @@ import ( type MockedClient struct { 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) - 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) 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) 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) - 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) 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 } -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) return args.Get(0).(io.ReadCloser), args.Error(1) } -func (m *MockedClient) Host() docker.Host { +func (m *MockedClient) Host() container.Host { args := m.Called() - return args.Get(0).(docker.Host) + return args.Get(0).(container.Host) } func (m *MockedClient) IsSwarmMode() bool { @@ -70,14 +70,14 @@ func (m *MockedClient) SystemInfo() system.Info { 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 { client = new(MockedClient) - client.(*MockedClient).On("ListContainers", mock.Anything, mock.Anything).Return([]docker.Container{}, nil) - client.(*MockedClient).On("Host").Return(docker.Host{ + client.(*MockedClient).On("ListContainers", mock.Anything, mock.Anything).Return([]container.Container{}, nil) + client.(*MockedClient).On("Host").Return(container.Host{ 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 { @@ -86,7 +86,7 @@ func createHandler(client docker.Client, content fs.FS, config Config) *chi.Mux 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) return createRouter(&handler{ 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}}) } diff --git a/main.go b/main.go index 59b3c62b..56d83965 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,7 @@ import ( "github.com/amir20/dozzle/internal/agent" "github.com/amir20/dozzle/internal/auth" + "github.com/amir20/dozzle/internal/container" "github.com/amir20/dozzle/internal/docker" "github.com/amir20/dozzle/internal/healthcheck" "github.com/amir20/dozzle/internal/support/cli" @@ -161,7 +162,7 @@ func main() { var multiHostService *docker_support.MultiHostService if args.Mode == "server" { - var localClient docker.Client + var localClient container.Client localClient, multiHostService = cli.CreateMultiHostService(certs, args) if multiHostService.TotalClients() == 0 { log.Fatal().Msg("Could not connect to any Docker Engine")