diff --git a/README.md b/README.md index bd439158..a882e15f 100644 --- a/README.md +++ b/README.md @@ -39,24 +39,24 @@ will bind to `localhost` on port `1224`. You can then use a reverse proxy to con #### Changing base URL dozzle by default mounts to "/". If you want to control the base path you can use the `--base` option. For example, if you want to mount at "/foobar", -then you can override by using `--base /foobar`. See env variables below for using `DOZZLE_BASE` to change this. +then you can override by using `--base /foobar`. See env variables below for using `DOZZLE_BASE` to change this. $ docker run --volume=/var/run/docker.sock:/var/run/docker.sock -p 8080:8080 amir20/dozzle:latest --base /foobar dozzle will be available at [http://localhost:8080/foobar/](http://localhost:8080/foobar/). -#### Environment variables and configuration +#### Environment variables and configuration -Dozzle follows the [12-factor](https://12factor.net/) model. Configurations can use the CLI flags or enviroment variables. The table below outlines all supported options and their respective env vars. +Dozzle follows the [12-factor](https://12factor.net/) model. Configurations can use the CLI flags or enviroment variables. The table below outlines all supported options and their respective env vars. -| Flag | Env Variable | Default | -| --- | --- | --- | +| Flag | Env Variable | Default | +| --- | --- | --- | | `--addr` | `DOZZLE_ADDR` | `:8080` | | `--base` | `DOZZLE_BASE` | `/` | | `--level` | `DOZZLE_LEVEL` | `info` | | n/a | `DOCKER_API_VERSION` | `1.38` | -| n/a | `TAIL_SIZE` | `300` | +| `--tailSize` | `DOZZLE_TAILSIZE` | `300` | ## License diff --git a/docker/client.go b/docker/client.go index 014d139f..daa8ac3e 100644 --- a/docker/client.go +++ b/docker/client.go @@ -1,6 +1,7 @@ package docker import ( + "strconv" "bytes" "context" "encoding/binary" @@ -9,7 +10,6 @@ import ( "github.com/docker/docker/client" "io" "log" - "os" "sort" "strings" ) @@ -27,8 +27,8 @@ type dockerProxy interface { // Client is a proxy around the docker client type Client interface { ListContainers() ([]Container, error) - ContainerLogs(ctx context.Context, id string) (<-chan string, <-chan error) - Events(ctx context.Context) (<-chan events.Message, <-chan error) + ContainerLogs(context.Context, string, int) (<-chan string, <-chan error) + Events(context.Context) (<-chan events.Message, <-chan error) } // NewClient creates a new instance of Client @@ -74,12 +74,8 @@ func (d *dockerClient) ListContainers() ([]Container, error) { return containers, nil } -func (d *dockerClient) ContainerLogs(ctx context.Context, id string) (<-chan string, <-chan error) { - tail := "300" - if value, ok := os.LookupEnv("TAIL_SIZE"); ok { - tail = value - } - options := types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: tail, Timestamps: true} +func (d *dockerClient) ContainerLogs(ctx context.Context, id string, tailSize int) (<-chan string, <-chan error) { + options := types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: strconv.Itoa(tailSize), Timestamps: true} reader, err := d.cli.ContainerLogs(ctx, id, options) errChannel := make(chan error, 1) diff --git a/docker/client_test.go b/docker/client_test.go index acb1ea87..af1aab0a 100644 --- a/docker/client_test.go +++ b/docker/client_test.go @@ -30,7 +30,7 @@ func (m *mockedProxy) ContainerList(context.Context, types.ContainerListOptions) } func (m *mockedProxy) ContainerLogs(ctx context.Context, id string, options types.ContainerLogsOptions) (io.ReadCloser, error) { - args := m.Called(ctx, id) + args := m.Called(ctx, id, options) reader, ok := args.Get(0).(io.ReadCloser) if !ok && args.Get(0) != nil { panic("reader is not of type io.ReadCloser") @@ -109,10 +109,11 @@ func Test_dockerClient_ContainerLogs_happy(t *testing.T) { var reader io.ReadCloser reader = ioutil.NopCloser(bytes.NewReader(b)) - proxy.On("ContainerLogs", mock.Anything, id, mock.Anything).Return(reader, nil) + options := types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: "300", Timestamps: true} + proxy.On("ContainerLogs", mock.Anything, id, options).Return(reader, nil) client := &dockerClient{proxy} - messages, _ := client.ContainerLogs(context.Background(), id) + messages, _ := client.ContainerLogs(context.Background(), id, 300) actual, _ := <-messages assert.Equal(t, expected, actual, "message doesn't match expected") @@ -125,10 +126,12 @@ func Test_dockerClient_ContainerLogs_happy(t *testing.T) { func Test_dockerClient_ContainerLogs_error(t *testing.T) { id := "123456" proxy := new(mockedProxy) + proxy.On("ContainerLogs", mock.Anything, id, mock.Anything).Return(nil, errors.New("test")) client := &dockerClient{proxy} - messages, err := client.ContainerLogs(context.Background(), id) + + messages, err := client.ContainerLogs(context.Background(), id, 300) assert.Nil(t, messages, "messages should be nil") diff --git a/main.go b/main.go index 3febd56d..3a6d8152 100644 --- a/main.go +++ b/main.go @@ -23,6 +23,7 @@ var ( addr = "" base = "" level = "" + tailSize = 300 version = "dev" commit = "none" date = "unknown" @@ -37,6 +38,7 @@ func init() { pflag.String("addr", ":8080", "http service address") pflag.String("base", "/", "base address of the application to mount") pflag.String("level", "info", "logging level") + pflag.Int("tailSize", 300, "Tail size to use for initial container logs") pflag.Parse() viper.AutomaticEnv() @@ -46,6 +48,7 @@ func init() { addr = viper.GetString("addr") base = viper.GetString("base") level = viper.GetString("level") + tailSize = viper.GetInt("tailSize") l, _ := log.ParseLevel(level) log.SetLevel(l) @@ -153,8 +156,7 @@ func (h *handler) streamLogs(w http.ResponseWriter, r *http.Request) { http.Error(w, "Streaming unsupported!", http.StatusInternalServerError) return } - - messages, err := h.client.ContainerLogs(r.Context(), id) + messages, err := h.client.ContainerLogs(r.Context(), id, tailSize) w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") diff --git a/main_test.go b/main_test.go index c38413ad..543b1da2 100644 --- a/main_test.go +++ b/main_test.go @@ -31,8 +31,8 @@ func (m *MockedClient) ListContainers() ([]docker.Container, error) { return containers, args.Error(1) } -func (m *MockedClient) ContainerLogs(ctx context.Context, id string) (<-chan string, <-chan error) { - args := m.Called(ctx, id) +func (m *MockedClient) ContainerLogs(ctx context.Context, id string, tailSize int) (<-chan string, <-chan error) { + args := m.Called(ctx, id, tailSize) channel, ok := args.Get(0).(chan string) if !ok { panic("channel is not of type chan string") @@ -91,7 +91,7 @@ func Test_handler_streamLogs_happy(t *testing.T) { id := "123456" req, err := http.NewRequest("GET", "/api/logs/stream", nil) q := req.URL.Query() - q.Add("id", "123456") + q.Add("id", id) req.URL.RawQuery = q.Encode() require.NoError(t, err, "NewRequest should not return an error.") @@ -101,7 +101,7 @@ func Test_handler_streamLogs_happy(t *testing.T) { messages := make(chan string) errChannel := make(chan error) - mockedClient.On("ContainerLogs", mock.Anything, id).Return(messages, errChannel) + mockedClient.On("ContainerLogs", mock.Anything, id, 300).Return(messages, errChannel) go func() { messages <- "INFO Testing logs..." close(messages) @@ -126,7 +126,7 @@ func Test_handler_streamLogs_error_reading(t *testing.T) { mockedClient := new(MockedClient) messages := make(chan string) errChannel := make(chan error) - mockedClient.On("ContainerLogs", mock.Anything, id).Return(messages, errChannel) + mockedClient.On("ContainerLogs", mock.Anything, id, 300).Return(messages, errChannel) go func() { errChannel <- errors.New("test error")