mirror of
https://github.com/amir20/dozzle.git
synced 2025-12-21 13:23:07 +01:00
feat: show start event for containers (#3014)
This commit is contained in:
1
assets/components.d.ts
vendored
1
assets/components.d.ts
vendored
@@ -25,6 +25,7 @@ declare module 'vue' {
|
|||||||
'Cil:xCircle': typeof import('~icons/cil/x-circle')['default']
|
'Cil:xCircle': typeof import('~icons/cil/x-circle')['default']
|
||||||
ComplexLogItem: typeof import('./components/LogViewer/ComplexLogItem.vue')['default']
|
ComplexLogItem: typeof import('./components/LogViewer/ComplexLogItem.vue')['default']
|
||||||
ContainerActionsToolbar: typeof import('./components/ContainerViewer/ContainerActionsToolbar.vue')['default']
|
ContainerActionsToolbar: typeof import('./components/ContainerViewer/ContainerActionsToolbar.vue')['default']
|
||||||
|
ContainerEventLogItem: typeof import('./components/LogViewer/ContainerEventLogItem.vue')['default']
|
||||||
ContainerHealth: typeof import('./components/ContainerViewer/ContainerHealth.vue')['default']
|
ContainerHealth: typeof import('./components/ContainerViewer/ContainerHealth.vue')['default']
|
||||||
ContainerLog: typeof import('./components/ContainerViewer/ContainerLog.vue')['default']
|
ContainerLog: typeof import('./components/ContainerViewer/ContainerLog.vue')['default']
|
||||||
ContainerName: typeof import('./components/LogViewer/ContainerName.vue')['default']
|
ContainerName: typeof import('./components/LogViewer/ContainerName.vue')['default']
|
||||||
|
|||||||
@@ -28,13 +28,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { DockerEventLogEntry } from "@/models/LogEntry";
|
import { ContainerEventLogEntry } from "@/models/LogEntry";
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { showToast } = useToast();
|
const { showToast } = useToast();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const { logEntry } = defineProps<{
|
const { logEntry } = defineProps<{
|
||||||
logEntry: DockerEventLogEntry;
|
logEntry: ContainerEventLogEntry;
|
||||||
showContainerName?: boolean;
|
showContainerName?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
type JSONObject,
|
type JSONObject,
|
||||||
LogEntry,
|
LogEntry,
|
||||||
asLogEntry,
|
asLogEntry,
|
||||||
DockerEventLogEntry,
|
ContainerEventLogEntry,
|
||||||
SkippedLogsEntry,
|
SkippedLogsEntry,
|
||||||
} from "@/models/LogEntry";
|
} from "@/models/LogEntry";
|
||||||
import { Service, Stack } from "@/models/Stack";
|
import { Service, Stack } from "@/models/Stack";
|
||||||
@@ -162,9 +162,15 @@ function useLogStream(url: Ref<string>, loadMoreUrl?: Ref<string>) {
|
|||||||
|
|
||||||
es = new EventSource(url.value);
|
es = new EventSource(url.value);
|
||||||
|
|
||||||
es.addEventListener("container-stopped", (e) => {
|
es.addEventListener("container-event", (e) => {
|
||||||
const event = JSON.parse((e as MessageEvent).data) as { actorId: string };
|
const event = JSON.parse((e as MessageEvent).data) as { actorId: string; name: string };
|
||||||
buffer.push(new DockerEventLogEntry("Container stopped", event.actorId, new Date(), "container-stopped"));
|
const containerEvent = new ContainerEventLogEntry(
|
||||||
|
event.name == "container-started" ? "Container started" : "Container stopped",
|
||||||
|
event.actorId,
|
||||||
|
new Date(),
|
||||||
|
event.name as "container-stopped" | "container-started",
|
||||||
|
);
|
||||||
|
buffer.push(containerEvent);
|
||||||
|
|
||||||
flushBuffer();
|
flushBuffer();
|
||||||
flushBuffer.flush();
|
flushBuffer.flush();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Component, ComputedRef, Ref } from "vue";
|
|||||||
import { flattenJSON, getDeep } from "@/utils";
|
import { flattenJSON, getDeep } from "@/utils";
|
||||||
import ComplexLogItem from "@/components/LogViewer/ComplexLogItem.vue";
|
import ComplexLogItem from "@/components/LogViewer/ComplexLogItem.vue";
|
||||||
import SimpleLogItem from "@/components/LogViewer/SimpleLogItem.vue";
|
import SimpleLogItem from "@/components/LogViewer/SimpleLogItem.vue";
|
||||||
import DockerEventLogItem from "@/components/LogViewer/DockerEventLogItem.vue";
|
import ContainerEventLogItem from "@/components/LogViewer/ContainerEventLogItem.vue";
|
||||||
import SkippedEntriesLogItem from "@/components/LogViewer/SkippedEntriesLogItem.vue";
|
import SkippedEntriesLogItem from "@/components/LogViewer/SkippedEntriesLogItem.vue";
|
||||||
|
|
||||||
export type JSONValue = string | number | boolean | JSONObject | Array<JSONValue>;
|
export type JSONValue = string | number | boolean | JSONObject | Array<JSONValue>;
|
||||||
@@ -107,7 +107,7 @@ export class ComplexLogEntry extends LogEntry<JSONObject> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DockerEventLogEntry extends LogEntry<string> {
|
export class ContainerEventLogEntry extends LogEntry<string> {
|
||||||
constructor(
|
constructor(
|
||||||
message: string,
|
message: string,
|
||||||
containerID: string,
|
containerID: string,
|
||||||
@@ -117,7 +117,7 @@ export class DockerEventLogEntry extends LogEntry<string> {
|
|||||||
super(message, containerID, date.getTime(), date, "stderr", "info");
|
super(message, containerID, date.getTime(), date, "stderr", "info");
|
||||||
}
|
}
|
||||||
getComponent(): Component {
|
getComponent(): Component {
|
||||||
return DockerEventLogItem;
|
return ContainerEventLogItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ export const useContainerStore = defineStore("container", () => {
|
|||||||
...newContainers.map((c) => {
|
...newContainers.map((c) => {
|
||||||
return new Container(
|
return new Container(
|
||||||
c.id,
|
c.id,
|
||||||
new Date(c.created * 1000),
|
new Date(c.created),
|
||||||
c.image,
|
c.image,
|
||||||
c.name,
|
c.name,
|
||||||
c.command,
|
c.command,
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ type DockerCLI interface {
|
|||||||
type Client interface {
|
type Client interface {
|
||||||
ListContainers() ([]Container, error)
|
ListContainers() ([]Container, error)
|
||||||
FindContainer(string) (Container, error)
|
FindContainer(string) (Container, error)
|
||||||
ContainerLogs(context.Context, string, string, StdType) (io.ReadCloser, error)
|
ContainerLogs(context.Context, string, *time.Time, StdType) (io.ReadCloser, error)
|
||||||
Events(context.Context, chan<- ContainerEvent) error
|
Events(context.Context, chan<- ContainerEvent) error
|
||||||
ContainerLogsBetweenDates(context.Context, string, time.Time, time.Time, StdType) (io.ReadCloser, error)
|
ContainerLogsBetweenDates(context.Context, string, time.Time, time.Time, StdType) (io.ReadCloser, error)
|
||||||
ContainerStats(context.Context, string, chan<- ContainerStat) error
|
ContainerStats(context.Context, string, chan<- ContainerStat) error
|
||||||
@@ -177,6 +177,10 @@ func (d *httpClient) FindContainer(id string) (Container, error) {
|
|||||||
|
|
||||||
if json, err := d.cli.ContainerInspect(context.Background(), container.ID); err == nil {
|
if json, err := d.cli.ContainerInspect(context.Background(), container.ID); err == nil {
|
||||||
container.Tty = json.Config.Tty
|
container.Tty = json.Config.Tty
|
||||||
|
if startedAt, err := time.Parse(time.RFC3339Nano, json.State.StartedAt); err == nil {
|
||||||
|
utc := startedAt.UTC()
|
||||||
|
container.StartedAt = &utc
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return container, err
|
return container, err
|
||||||
}
|
}
|
||||||
@@ -226,7 +230,7 @@ func (d *httpClient) ListContainers() ([]Container, error) {
|
|||||||
Image: c.Image,
|
Image: c.Image,
|
||||||
ImageID: c.ImageID,
|
ImageID: c.ImageID,
|
||||||
Command: c.Command,
|
Command: c.Command,
|
||||||
Created: c.Created,
|
Created: time.Unix(c.Created, 0),
|
||||||
State: c.State,
|
State: c.State,
|
||||||
Status: c.Status,
|
Status: c.Status,
|
||||||
Host: d.host.ID,
|
Host: d.host.ID,
|
||||||
@@ -295,15 +299,12 @@ func (d *httpClient) ContainerStats(ctx context.Context, id string, stats chan<-
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *httpClient) ContainerLogs(ctx context.Context, id string, since string, stdType StdType) (io.ReadCloser, error) {
|
func (d *httpClient) ContainerLogs(ctx context.Context, id string, since *time.Time, stdType StdType) (io.ReadCloser, error) {
|
||||||
log.WithField("id", id).WithField("since", since).WithField("stdType", stdType).Debug("streaming logs for container")
|
log.WithField("id", id).WithField("since", since).WithField("stdType", stdType).Debug("streaming logs for container")
|
||||||
|
|
||||||
if since != "" {
|
sinceQuery := ""
|
||||||
if millis, err := strconv.ParseInt(since, 10, 64); err == nil {
|
if since != nil {
|
||||||
since = time.UnixMicro(millis).Add(time.Millisecond).Format(time.RFC3339Nano)
|
sinceQuery = since.Add(time.Millisecond).Format(time.RFC3339Nano)
|
||||||
} else {
|
|
||||||
log.WithError(err).Debug("unable to parse since")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
options := container.LogsOptions{
|
options := container.LogsOptions{
|
||||||
@@ -312,7 +313,7 @@ func (d *httpClient) ContainerLogs(ctx context.Context, id string, since string,
|
|||||||
Follow: true,
|
Follow: true,
|
||||||
Tail: strconv.Itoa(100),
|
Tail: strconv.Itoa(100),
|
||||||
Timestamps: true,
|
Timestamps: true,
|
||||||
Since: since,
|
Since: sinceQuery,
|
||||||
}
|
}
|
||||||
|
|
||||||
reader, err := d.cli.ContainerLogs(ctx, id, options)
|
reader, err := d.cli.ContainerLogs(ctx, id, options)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -149,11 +150,18 @@ func Test_dockerClient_ContainerLogs_happy(t *testing.T) {
|
|||||||
b = append(b, []byte(expected)...)
|
b = append(b, []byte(expected)...)
|
||||||
|
|
||||||
reader := io.NopCloser(bytes.NewReader(b))
|
reader := io.NopCloser(bytes.NewReader(b))
|
||||||
options := container.LogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: "100", Timestamps: true, Since: "since"}
|
since := time.Date(2021, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
options := container.LogsOptions{
|
||||||
|
ShowStdout: true,
|
||||||
|
ShowStderr: true,
|
||||||
|
Follow: true,
|
||||||
|
Tail: "100",
|
||||||
|
Timestamps: true,
|
||||||
|
Since: "2021-01-01T00:00:00.001Z"}
|
||||||
proxy.On("ContainerLogs", mock.Anything, id, options).Return(reader, nil)
|
proxy.On("ContainerLogs", mock.Anything, id, options).Return(reader, nil)
|
||||||
|
|
||||||
client := &httpClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}, system.Info{}}
|
client := &httpClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}, system.Info{}}
|
||||||
logReader, _ := client.ContainerLogs(context.Background(), id, "since", STDALL)
|
logReader, _ := client.ContainerLogs(context.Background(), id, &since, STDALL)
|
||||||
|
|
||||||
actual, _ := io.ReadAll(logReader)
|
actual, _ := io.ReadAll(logReader)
|
||||||
assert.Equal(t, string(b), string(actual), "message doesn't match expected")
|
assert.Equal(t, string(b), string(actual), "message doesn't match expected")
|
||||||
@@ -168,7 +176,7 @@ func Test_dockerClient_ContainerLogs_error(t *testing.T) {
|
|||||||
|
|
||||||
client := &httpClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}, system.Info{}}
|
client := &httpClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}, system.Info{}}
|
||||||
|
|
||||||
reader, err := client.ContainerLogs(context.Background(), id, "", STDALL)
|
reader, err := client.ContainerLogs(context.Background(), id, nil, STDALL)
|
||||||
|
|
||||||
assert.Nil(t, reader, "reader should be nil")
|
assert.Nil(t, reader, "reader should be nil")
|
||||||
assert.Error(t, err, "error should have been returned")
|
assert.Error(t, err, "error should have been returned")
|
||||||
@@ -190,7 +198,8 @@ func Test_dockerClient_FindContainer_happy(t *testing.T) {
|
|||||||
proxy := new(mockedProxy)
|
proxy := new(mockedProxy)
|
||||||
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(containers, nil)
|
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(containers, nil)
|
||||||
|
|
||||||
json := types.ContainerJSON{Config: &container.Config{Tty: false}}
|
state := &types.ContainerState{Status: "running", StartedAt: time.Now().Format(time.RFC3339Nano)}
|
||||||
|
json := types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{State: state}, Config: &container.Config{Tty: false}}
|
||||||
proxy.On("ContainerInspect", mock.Anything, "abcdefghijkl").Return(json, nil)
|
proxy.On("ContainerInspect", mock.Anything, "abcdefghijkl").Return(json, nil)
|
||||||
|
|
||||||
client := &httpClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}, system.Info{}}
|
client := &httpClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}, system.Info{}}
|
||||||
@@ -238,7 +247,10 @@ func Test_dockerClient_ContainerActions_happy(t *testing.T) {
|
|||||||
|
|
||||||
proxy := new(mockedProxy)
|
proxy := new(mockedProxy)
|
||||||
client := &httpClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}, system.Info{}}
|
client := &httpClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}, system.Info{}}
|
||||||
json := types.ContainerJSON{Config: &container.Config{Tty: false}}
|
|
||||||
|
state := &types.ContainerState{Status: "running", StartedAt: time.Now().Format(time.RFC3339Nano)}
|
||||||
|
json := types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{State: state}, Config: &container.Config{Tty: false}}
|
||||||
|
|
||||||
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(containers, nil)
|
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(containers, nil)
|
||||||
proxy.On("ContainerInspect", mock.Anything, "abcdefghijkl").Return(json, nil)
|
proxy.On("ContainerInspect", mock.Anything, "abcdefghijkl").Return(json, nil)
|
||||||
proxy.On("ContainerStart", mock.Anything, "abcdefghijkl", mock.Anything).Return(nil)
|
proxy.On("ContainerStart", mock.Anything, "abcdefghijkl", mock.Anything).Return(nil)
|
||||||
|
|||||||
@@ -2,27 +2,29 @@ package docker
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/internal/utils"
|
"github.com/amir20/dozzle/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Container represents an internal representation of docker containers
|
// Container represents an internal representation of docker containers
|
||||||
type Container struct {
|
type Container struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Names []string `json:"names"`
|
Names []string `json:"names"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Image string `json:"image"`
|
Image string `json:"image"`
|
||||||
ImageID string `json:"imageId"`
|
ImageID string `json:"imageId"`
|
||||||
Command string `json:"command"`
|
Command string `json:"command"`
|
||||||
Created int64 `json:"created"`
|
Created time.Time `json:"created"`
|
||||||
State string `json:"state"`
|
StartedAt *time.Time `json:"startedAt,omitempty"`
|
||||||
Status string `json:"status"`
|
State string `json:"state"`
|
||||||
Health string `json:"health,omitempty"`
|
Status string `json:"status"`
|
||||||
Host string `json:"host,omitempty"`
|
Health string `json:"health,omitempty"`
|
||||||
Tty bool `json:"-"`
|
Host string `json:"host,omitempty"`
|
||||||
Labels map[string]string `json:"labels,omitempty"`
|
Tty bool `json:"-"`
|
||||||
Stats *utils.RingBuffer[ContainerStat] `json:"stats,omitempty"`
|
Labels map[string]string `json:"labels,omitempty"`
|
||||||
Group string `json:"group,omitempty"`
|
Stats *utils.RingBuffer[ContainerStat] `json:"stats,omitempty"`
|
||||||
|
Group string `json:"group,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerStat represent stats instant for a container
|
// ContainerStat represent stats instant for a container
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ data: []
|
|||||||
|
|
||||||
|
|
||||||
event: containers-changed
|
event: containers-changed
|
||||||
data: [{"id":"1234","names":null,"name":"test","image":"test","imageId":"","command":"","created":0,"state":"","status":"","stats":[]}]
|
data: [{"id":"1234","names":null,"name":"test","image":"test","imageId":"","command":"","created":"0001-01-01T00:00:00Z","state":"","status":"","stats":[]}]
|
||||||
|
|
||||||
|
|
||||||
event: container-start
|
event: container-start
|
||||||
@@ -178,7 +178,7 @@ X-Accel-Buffering: no
|
|||||||
|
|
||||||
data: {"m":"INFO Testing logs...","ts":0,"id":4256192898,"l":"info","s":"stdout","c":"123456"}
|
data: {"m":"INFO Testing logs...","ts":0,"id":4256192898,"l":"info","s":"stdout","c":"123456"}
|
||||||
|
|
||||||
event: container-stopped
|
event: container-event
|
||||||
data: {"actorId":"123456","name":"container-stopped","host":"localhost"}
|
data: {"actorId":"123456","name":"container-stopped","host":"localhost"}
|
||||||
|
|
||||||
/* snapshot: Test_handler_streamLogs_happy_container_stopped */
|
/* snapshot: Test_handler_streamLogs_happy_container_stopped */
|
||||||
@@ -204,5 +204,5 @@ X-Accel-Buffering: no
|
|||||||
data: {"m":"INFO Testing logs...","ts":1589396137772,"id":1469707724,"l":"info","s":"stdout","c":"123456"}
|
data: {"m":"INFO Testing logs...","ts":1589396137772,"id":1469707724,"l":"info","s":"stdout","c":"123456"}
|
||||||
id: 1589396137772
|
id: 1589396137772
|
||||||
|
|
||||||
event: container-stopped
|
event: container-event
|
||||||
data: {"actorId":"123456","name":"container-stopped","host":"localhost"}
|
data: {"actorId":"123456","name":"container-stopped","host":"localhost"}
|
||||||
@@ -343,11 +343,13 @@ func streamLogsForContainers(w http.ResponseWriter, r *http.Request, clients map
|
|||||||
w.Header().Set("X-Accel-Buffering", "no")
|
w.Header().Set("X-Accel-Buffering", "no")
|
||||||
|
|
||||||
logs := make(chan *docker.LogEvent)
|
logs := make(chan *docker.LogEvent)
|
||||||
events := make(chan *docker.ContainerEvent)
|
events := make(chan *docker.ContainerEvent, 1)
|
||||||
|
|
||||||
ticker := time.NewTicker(5 * time.Second)
|
ticker := time.NewTicker(5 * time.Second)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
started := time.Now()
|
||||||
|
|
||||||
loop:
|
loop:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@@ -366,8 +368,11 @@ loop:
|
|||||||
fmt.Fprintf(w, ":ping \n\n")
|
fmt.Fprintf(w, ":ping \n\n")
|
||||||
f.Flush()
|
f.Flush()
|
||||||
case container := <-containers:
|
case container := <-containers:
|
||||||
|
if container.StartedAt != nil && container.StartedAt.After(started) {
|
||||||
|
events <- &docker.ContainerEvent{ActorID: container.ID, Name: "container-started", Host: container.Host}
|
||||||
|
}
|
||||||
go func(container docker.Container) {
|
go func(container docker.Container) {
|
||||||
reader, err := clients[container.Host].ContainerLogs(r.Context(), container.ID, "", stdTypes)
|
reader, err := clients[container.Host].ContainerLogs(r.Context(), container.ID, container.StartedAt, stdTypes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -395,7 +400,7 @@ loop:
|
|||||||
if buf, err := json.Marshal(event); err != nil {
|
if buf, err := json.Marshal(event); err != nil {
|
||||||
log.Errorf("json encoding error while streaming %v", err.Error())
|
log.Errorf("json encoding error while streaming %v", err.Error())
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(w, "event: container-stopped\ndata: %s\n\n", buf)
|
fmt.Fprintf(w, "event: container-event\ndata: %s\n\n", buf)
|
||||||
f.Flush()
|
f.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,10 @@ func Test_handler_streamLogs_happy(t *testing.T) {
|
|||||||
|
|
||||||
data := makeMessage("INFO Testing logs...", docker.STDOUT)
|
data := makeMessage("INFO Testing logs...", docker.STDOUT)
|
||||||
|
|
||||||
mockedClient.On("FindContainer", id).Return(docker.Container{ID: id, Tty: false, Host: "localhost"}, nil)
|
now := time.Now()
|
||||||
mockedClient.On("ContainerLogs", mock.Anything, mock.Anything, "", docker.STDALL).Return(io.NopCloser(bytes.NewReader(data)), nil).
|
|
||||||
|
mockedClient.On("FindContainer", 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).
|
||||||
Run(func(args mock.Arguments) {
|
Run(func(args mock.Arguments) {
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
@@ -67,8 +69,10 @@ func Test_handler_streamLogs_happy_with_id(t *testing.T) {
|
|||||||
|
|
||||||
data := makeMessage("2020-05-13T18:55:37.772853839Z INFO Testing logs...", docker.STDOUT)
|
data := makeMessage("2020-05-13T18:55:37.772853839Z INFO Testing logs...", docker.STDOUT)
|
||||||
|
|
||||||
mockedClient.On("FindContainer", id).Return(docker.Container{ID: id, Host: "localhost"}, nil)
|
started := time.Date(2020, time.May, 13, 18, 55, 37, 772853839, time.UTC)
|
||||||
mockedClient.On("ContainerLogs", mock.Anything, mock.Anything, "", docker.STDALL).Return(io.NopCloser(bytes.NewReader(data)), nil).
|
|
||||||
|
mockedClient.On("FindContainer", 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).
|
||||||
Run(func(args mock.Arguments) {
|
Run(func(args mock.Arguments) {
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
@@ -94,9 +98,10 @@ func Test_handler_streamLogs_happy_container_stopped(t *testing.T) {
|
|||||||
req.URL.RawQuery = q.Encode()
|
req.URL.RawQuery = q.Encode()
|
||||||
require.NoError(t, err, "NewRequest should not return an error.")
|
require.NoError(t, err, "NewRequest should not return an error.")
|
||||||
|
|
||||||
|
started := time.Date(2020, time.May, 13, 18, 55, 37, 772853839, time.UTC)
|
||||||
mockedClient := new(MockedClient)
|
mockedClient := new(MockedClient)
|
||||||
mockedClient.On("FindContainer", id).Return(docker.Container{ID: id, Host: "localhost"}, nil)
|
mockedClient.On("FindContainer", id).Return(docker.Container{ID: id, Host: "localhost", StartedAt: &started}, nil)
|
||||||
mockedClient.On("ContainerLogs", mock.Anything, id, "", docker.STDALL).Return(io.NopCloser(strings.NewReader("")), io.EOF).
|
mockedClient.On("ContainerLogs", mock.Anything, id, &started, docker.STDALL).Return(io.NopCloser(strings.NewReader("")), io.EOF).
|
||||||
Run(func(args mock.Arguments) {
|
Run(func(args mock.Arguments) {
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
@@ -150,9 +155,10 @@ func Test_handler_streamLogs_error_reading(t *testing.T) {
|
|||||||
req.URL.RawQuery = q.Encode()
|
req.URL.RawQuery = q.Encode()
|
||||||
require.NoError(t, err, "NewRequest should not return an error.")
|
require.NoError(t, err, "NewRequest should not return an error.")
|
||||||
|
|
||||||
|
started := time.Date(2020, time.May, 13, 18, 55, 37, 772853839, time.UTC)
|
||||||
mockedClient := new(MockedClient)
|
mockedClient := new(MockedClient)
|
||||||
mockedClient.On("FindContainer", id).Return(docker.Container{ID: id, Host: "localhost"}, nil)
|
mockedClient.On("FindContainer", id).Return(docker.Container{ID: id, Host: "localhost", StartedAt: &started}, nil)
|
||||||
mockedClient.On("ContainerLogs", mock.Anything, id, "", docker.STDALL).Return(io.NopCloser(strings.NewReader("")), errors.New("test error")).
|
mockedClient.On("ContainerLogs", mock.Anything, id, &started, docker.STDALL).Return(io.NopCloser(strings.NewReader("")), errors.New("test error")).
|
||||||
Run(func(args mock.Arguments) {
|
Run(func(args mock.Arguments) {
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func (m *MockedClient) ListContainers() ([]docker.Container, error) {
|
|||||||
return args.Get(0).([]docker.Container), args.Error(1)
|
return args.Get(0).([]docker.Container), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) ContainerLogs(ctx context.Context, id string, since string, stdType docker.StdType) (io.ReadCloser, error) {
|
func (m *MockedClient) ContainerLogs(ctx context.Context, id string, since *time.Time, stdType docker.StdType) (io.ReadCloser, error) {
|
||||||
args := m.Called(ctx, id, since, stdType)
|
args := m.Called(ctx, id, since, stdType)
|
||||||
return args.Get(0).(io.ReadCloser), args.Error(1)
|
return args.Get(0).(io.ReadCloser), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user