2019-06-20 18:54:12 +00:00
|
|
|
package docker_log
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"crypto/tls"
|
|
|
|
"io"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/docker/docker/api/types"
|
|
|
|
"github.com/docker/docker/api/types/container"
|
|
|
|
"github.com/docker/docker/pkg/stdcopy"
|
|
|
|
"github.com/influxdata/telegraf"
|
|
|
|
"github.com/influxdata/telegraf/internal"
|
|
|
|
"github.com/influxdata/telegraf/testutil"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
type MockClient struct {
|
|
|
|
ContainerListF func(ctx context.Context, options types.ContainerListOptions) ([]types.Container, error)
|
|
|
|
ContainerInspectF func(ctx context.Context, containerID string) (types.ContainerJSON, error)
|
|
|
|
ContainerLogsF func(ctx context.Context, containerID string, options types.ContainerLogsOptions) (io.ReadCloser, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *MockClient) ContainerList(
|
|
|
|
ctx context.Context,
|
|
|
|
options types.ContainerListOptions,
|
|
|
|
) ([]types.Container, error) {
|
|
|
|
return c.ContainerListF(ctx, options)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *MockClient) ContainerInspect(
|
|
|
|
ctx context.Context,
|
|
|
|
containerID string,
|
|
|
|
) (types.ContainerJSON, error) {
|
|
|
|
return c.ContainerInspectF(ctx, containerID)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *MockClient) ContainerLogs(
|
|
|
|
ctx context.Context,
|
|
|
|
containerID string,
|
|
|
|
options types.ContainerLogsOptions,
|
|
|
|
) (io.ReadCloser, error) {
|
|
|
|
return c.ContainerLogsF(ctx, containerID, options)
|
|
|
|
}
|
|
|
|
|
|
|
|
type Response struct {
|
|
|
|
io.Reader
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Response) Close() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-05-06 18:20:44 +00:00
|
|
|
func MustParse(layout, value string) time.Time {
|
|
|
|
tm, err := time.Parse(layout, value)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return tm
|
|
|
|
}
|
|
|
|
|
2019-06-20 18:54:12 +00:00
|
|
|
func Test(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
client *MockClient
|
|
|
|
expected []telegraf.Metric
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "no containers",
|
|
|
|
client: &MockClient{
|
|
|
|
ContainerListF: func(ctx context.Context, options types.ContainerListOptions) ([]types.Container, error) {
|
|
|
|
return nil, nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "one container tty",
|
|
|
|
client: &MockClient{
|
|
|
|
ContainerListF: func(ctx context.Context, options types.ContainerListOptions) ([]types.Container, error) {
|
|
|
|
return []types.Container{
|
|
|
|
{
|
|
|
|
ID: "deadbeef",
|
|
|
|
Names: []string{"/telegraf"},
|
|
|
|
Image: "influxdata/telegraf:1.11.0",
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
ContainerInspectF: func(ctx context.Context, containerID string) (types.ContainerJSON, error) {
|
|
|
|
return types.ContainerJSON{
|
|
|
|
Config: &container.Config{
|
|
|
|
Tty: true,
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
ContainerLogsF: func(ctx context.Context, containerID string, options types.ContainerLogsOptions) (io.ReadCloser, error) {
|
2020-05-06 18:20:44 +00:00
|
|
|
return &Response{Reader: bytes.NewBuffer([]byte("2020-04-28T18:43:16.432691200Z hello\n"))}, nil
|
2019-06-20 18:54:12 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
expected: []telegraf.Metric{
|
|
|
|
testutil.MustMetric(
|
|
|
|
"docker_log",
|
|
|
|
map[string]string{
|
|
|
|
"container_name": "telegraf",
|
|
|
|
"container_image": "influxdata/telegraf",
|
|
|
|
"container_version": "1.11.0",
|
|
|
|
"stream": "tty",
|
2019-10-08 00:27:32 +00:00
|
|
|
"source": "deadbeef",
|
2019-06-20 18:54:12 +00:00
|
|
|
},
|
|
|
|
map[string]interface{}{
|
|
|
|
"container_id": "deadbeef",
|
|
|
|
"message": "hello",
|
|
|
|
},
|
2020-05-06 18:20:44 +00:00
|
|
|
MustParse(time.RFC3339Nano, "2020-04-28T18:43:16.432691200Z"),
|
2019-06-20 18:54:12 +00:00
|
|
|
),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "one container multiplex",
|
|
|
|
client: &MockClient{
|
|
|
|
ContainerListF: func(ctx context.Context, options types.ContainerListOptions) ([]types.Container, error) {
|
|
|
|
return []types.Container{
|
|
|
|
{
|
|
|
|
ID: "deadbeef",
|
|
|
|
Names: []string{"/telegraf"},
|
|
|
|
Image: "influxdata/telegraf:1.11.0",
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
ContainerInspectF: func(ctx context.Context, containerID string) (types.ContainerJSON, error) {
|
|
|
|
return types.ContainerJSON{
|
|
|
|
Config: &container.Config{
|
|
|
|
Tty: false,
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
ContainerLogsF: func(ctx context.Context, containerID string, options types.ContainerLogsOptions) (io.ReadCloser, error) {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
w := stdcopy.NewStdWriter(&buf, stdcopy.Stdout)
|
2020-05-06 18:20:44 +00:00
|
|
|
w.Write([]byte("2020-04-28T18:42:16.432691200Z hello from stdout"))
|
2019-06-20 18:54:12 +00:00
|
|
|
return &Response{Reader: &buf}, nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expected: []telegraf.Metric{
|
|
|
|
testutil.MustMetric(
|
|
|
|
"docker_log",
|
|
|
|
map[string]string{
|
|
|
|
"container_name": "telegraf",
|
|
|
|
"container_image": "influxdata/telegraf",
|
|
|
|
"container_version": "1.11.0",
|
|
|
|
"stream": "stdout",
|
2019-10-08 00:27:32 +00:00
|
|
|
"source": "deadbeef",
|
2019-06-20 18:54:12 +00:00
|
|
|
},
|
|
|
|
map[string]interface{}{
|
|
|
|
"container_id": "deadbeef",
|
|
|
|
"message": "hello from stdout",
|
|
|
|
},
|
2020-05-06 18:20:44 +00:00
|
|
|
MustParse(time.RFC3339Nano, "2020-04-28T18:42:16.432691200Z"),
|
2019-06-20 18:54:12 +00:00
|
|
|
),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
var acc testutil.Accumulator
|
|
|
|
plugin := &DockerLogs{
|
2019-10-08 00:27:32 +00:00
|
|
|
Timeout: internal.Duration{Duration: time.Second * 5},
|
|
|
|
newClient: func(string, *tls.Config) (Client, error) { return tt.client, nil },
|
|
|
|
containerList: make(map[string]context.CancelFunc),
|
|
|
|
IncludeSourceTag: true,
|
2019-06-20 18:54:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
err := plugin.Init()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
err = plugin.Gather(&acc)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
acc.Wait(len(tt.expected))
|
|
|
|
plugin.Stop()
|
|
|
|
|
2020-05-06 18:20:44 +00:00
|
|
|
require.Nil(t, acc.Errors) // no errors during gathering
|
|
|
|
|
|
|
|
testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics())
|
2019-06-20 18:54:12 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|