521 lines
16 KiB
Go
521 lines
16 KiB
Go
package docker
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"testing"
|
|
|
|
"github.com/influxdata/telegraf/testutil"
|
|
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types/swarm"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type MockClient struct {
|
|
InfoF func(ctx context.Context) (types.Info, error)
|
|
ContainerListF func(ctx context.Context, options types.ContainerListOptions) ([]types.Container, error)
|
|
ContainerStatsF func(ctx context.Context, containerID string, stream bool) (types.ContainerStats, error)
|
|
ContainerInspectF func(ctx context.Context, containerID string) (types.ContainerJSON, error)
|
|
ServiceListF func(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error)
|
|
TaskListF func(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error)
|
|
NodeListF func(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error)
|
|
}
|
|
|
|
func (c *MockClient) Info(ctx context.Context) (types.Info, error) {
|
|
return c.InfoF(ctx)
|
|
}
|
|
|
|
func (c *MockClient) ContainerList(
|
|
ctx context.Context,
|
|
options types.ContainerListOptions,
|
|
) ([]types.Container, error) {
|
|
return c.ContainerListF(ctx, options)
|
|
}
|
|
|
|
func (c *MockClient) ContainerStats(
|
|
ctx context.Context,
|
|
containerID string,
|
|
stream bool,
|
|
) (types.ContainerStats, error) {
|
|
return c.ContainerStatsF(ctx, containerID, stream)
|
|
}
|
|
|
|
func (c *MockClient) ContainerInspect(
|
|
ctx context.Context,
|
|
containerID string,
|
|
) (types.ContainerJSON, error) {
|
|
return c.ContainerInspectF(ctx, containerID)
|
|
}
|
|
|
|
func (c *MockClient) ServiceList(
|
|
ctx context.Context,
|
|
options types.ServiceListOptions,
|
|
) ([]swarm.Service, error) {
|
|
return c.ServiceListF(ctx, options)
|
|
}
|
|
|
|
func (c *MockClient) TaskList(
|
|
ctx context.Context,
|
|
options types.TaskListOptions,
|
|
) ([]swarm.Task, error) {
|
|
return c.TaskListF(ctx, options)
|
|
}
|
|
|
|
func (c *MockClient) NodeList(
|
|
ctx context.Context,
|
|
options types.NodeListOptions,
|
|
) ([]swarm.Node, error) {
|
|
return c.NodeListF(ctx, options)
|
|
}
|
|
|
|
func newClient(host string, tlsConfig *tls.Config) (Client, error) {
|
|
return &MockClient{
|
|
InfoF: func(context.Context) (types.Info, error) {
|
|
return info, nil
|
|
},
|
|
ContainerListF: func(context.Context, types.ContainerListOptions) ([]types.Container, error) {
|
|
return containerList, nil
|
|
},
|
|
ContainerStatsF: func(context.Context, string, bool) (types.ContainerStats, error) {
|
|
return containerStats(), nil
|
|
},
|
|
ContainerInspectF: func(context.Context, string) (types.ContainerJSON, error) {
|
|
return containerInspect, nil
|
|
},
|
|
ServiceListF: func(context.Context, types.ServiceListOptions) ([]swarm.Service, error) {
|
|
return ServiceList, nil
|
|
},
|
|
TaskListF: func(context.Context, types.TaskListOptions) ([]swarm.Task, error) {
|
|
return TaskList, nil
|
|
},
|
|
NodeListF: func(context.Context, types.NodeListOptions) ([]swarm.Node, error) {
|
|
return NodeList, nil
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func TestDockerGatherContainerStats(t *testing.T) {
|
|
var acc testutil.Accumulator
|
|
stats := testStats()
|
|
|
|
tags := map[string]string{
|
|
"container_name": "redis",
|
|
"container_image": "redis/image",
|
|
}
|
|
|
|
gatherContainerStats(stats, &acc, tags, "123456789", true, true, "linux")
|
|
|
|
// test docker_container_net measurement
|
|
netfields := map[string]interface{}{
|
|
"rx_dropped": uint64(1),
|
|
"rx_bytes": uint64(2),
|
|
"rx_errors": uint64(3),
|
|
"tx_packets": uint64(4),
|
|
"tx_dropped": uint64(1),
|
|
"rx_packets": uint64(2),
|
|
"tx_errors": uint64(3),
|
|
"tx_bytes": uint64(4),
|
|
"container_id": "123456789",
|
|
}
|
|
nettags := copyTags(tags)
|
|
nettags["network"] = "eth0"
|
|
acc.AssertContainsTaggedFields(t, "docker_container_net", netfields, nettags)
|
|
|
|
netfields = map[string]interface{}{
|
|
"rx_dropped": uint64(6),
|
|
"rx_bytes": uint64(8),
|
|
"rx_errors": uint64(10),
|
|
"tx_packets": uint64(12),
|
|
"tx_dropped": uint64(6),
|
|
"rx_packets": uint64(8),
|
|
"tx_errors": uint64(10),
|
|
"tx_bytes": uint64(12),
|
|
"container_id": "123456789",
|
|
}
|
|
nettags = copyTags(tags)
|
|
nettags["network"] = "total"
|
|
acc.AssertContainsTaggedFields(t, "docker_container_net", netfields, nettags)
|
|
|
|
// test docker_blkio measurement
|
|
blkiotags := copyTags(tags)
|
|
blkiotags["device"] = "6:0"
|
|
blkiofields := map[string]interface{}{
|
|
"io_service_bytes_recursive_read": uint64(100),
|
|
"io_serviced_recursive_write": uint64(101),
|
|
"container_id": "123456789",
|
|
}
|
|
acc.AssertContainsTaggedFields(t, "docker_container_blkio", blkiofields, blkiotags)
|
|
|
|
blkiotags = copyTags(tags)
|
|
blkiotags["device"] = "total"
|
|
blkiofields = map[string]interface{}{
|
|
"io_service_bytes_recursive_read": uint64(100),
|
|
"io_serviced_recursive_write": uint64(302),
|
|
"container_id": "123456789",
|
|
}
|
|
acc.AssertContainsTaggedFields(t, "docker_container_blkio", blkiofields, blkiotags)
|
|
|
|
// test docker_container_mem measurement
|
|
memfields := map[string]interface{}{
|
|
"active_anon": uint64(0),
|
|
"active_file": uint64(1),
|
|
"cache": uint64(0),
|
|
"container_id": "123456789",
|
|
"fail_count": uint64(1),
|
|
"hierarchical_memory_limit": uint64(0),
|
|
"inactive_anon": uint64(0),
|
|
"inactive_file": uint64(3),
|
|
"limit": uint64(2000),
|
|
"mapped_file": uint64(0),
|
|
"max_usage": uint64(1001),
|
|
"pgfault": uint64(2),
|
|
"pgmajfault": uint64(0),
|
|
"pgpgin": uint64(0),
|
|
"pgpgout": uint64(0),
|
|
"rss_huge": uint64(0),
|
|
"rss": uint64(0),
|
|
"total_active_anon": uint64(0),
|
|
"total_active_file": uint64(0),
|
|
"total_cache": uint64(0),
|
|
"total_inactive_anon": uint64(0),
|
|
"total_inactive_file": uint64(0),
|
|
"total_mapped_file": uint64(0),
|
|
"total_pgfault": uint64(0),
|
|
"total_pgmajfault": uint64(0),
|
|
"total_pgpgin": uint64(4),
|
|
"total_pgpgout": uint64(0),
|
|
"total_rss_huge": uint64(444),
|
|
"total_rss": uint64(44),
|
|
"total_unevictable": uint64(0),
|
|
"total_writeback": uint64(55),
|
|
"unevictable": uint64(0),
|
|
"usage_percent": float64(55.55),
|
|
"usage": uint64(1111),
|
|
"writeback": uint64(0),
|
|
}
|
|
|
|
acc.AssertContainsTaggedFields(t, "docker_container_mem", memfields, tags)
|
|
|
|
// test docker_container_cpu measurement
|
|
cputags := copyTags(tags)
|
|
cputags["cpu"] = "cpu-total"
|
|
cpufields := map[string]interface{}{
|
|
"usage_total": uint64(500),
|
|
"usage_in_usermode": uint64(100),
|
|
"usage_in_kernelmode": uint64(200),
|
|
"usage_system": uint64(100),
|
|
"throttling_periods": uint64(1),
|
|
"throttling_throttled_periods": uint64(0),
|
|
"throttling_throttled_time": uint64(0),
|
|
"usage_percent": float64(400.0),
|
|
"container_id": "123456789",
|
|
}
|
|
acc.AssertContainsTaggedFields(t, "docker_container_cpu", cpufields, cputags)
|
|
|
|
cputags["cpu"] = "cpu0"
|
|
cpu0fields := map[string]interface{}{
|
|
"usage_total": uint64(1),
|
|
"container_id": "123456789",
|
|
}
|
|
acc.AssertContainsTaggedFields(t, "docker_container_cpu", cpu0fields, cputags)
|
|
|
|
cputags["cpu"] = "cpu1"
|
|
cpu1fields := map[string]interface{}{
|
|
"usage_total": uint64(1002),
|
|
"container_id": "123456789",
|
|
}
|
|
acc.AssertContainsTaggedFields(t, "docker_container_cpu", cpu1fields, cputags)
|
|
|
|
// Those tagged filed should not be present because of offline CPUs
|
|
cputags["cpu"] = "cpu2"
|
|
cpu2fields := map[string]interface{}{
|
|
"usage_total": uint64(0),
|
|
"container_id": "123456789",
|
|
}
|
|
acc.AssertDoesNotContainsTaggedFields(t, "docker_container_cpu", cpu2fields, cputags)
|
|
|
|
cputags["cpu"] = "cpu3"
|
|
cpu3fields := map[string]interface{}{
|
|
"usage_total": uint64(0),
|
|
"container_id": "123456789",
|
|
}
|
|
acc.AssertDoesNotContainsTaggedFields(t, "docker_container_cpu", cpu3fields, cputags)
|
|
}
|
|
|
|
func TestDocker_WindowsMemoryContainerStats(t *testing.T) {
|
|
var acc testutil.Accumulator
|
|
|
|
d := Docker{
|
|
newClient: func(string, *tls.Config) (Client, error) {
|
|
return &MockClient{
|
|
InfoF: func(ctx context.Context) (types.Info, error) {
|
|
return info, nil
|
|
},
|
|
ContainerListF: func(ctx context.Context, options types.ContainerListOptions) ([]types.Container, error) {
|
|
return containerList, nil
|
|
},
|
|
ContainerStatsF: func(ctx context.Context, containerID string, stream bool) (types.ContainerStats, error) {
|
|
return containerStatsWindows(), nil
|
|
},
|
|
ContainerInspectF: func(ctx context.Context, containerID string) (types.ContainerJSON, error) {
|
|
return containerInspect, nil
|
|
},
|
|
ServiceListF: func(context.Context, types.ServiceListOptions) ([]swarm.Service, error) {
|
|
return ServiceList, nil
|
|
},
|
|
TaskListF: func(context.Context, types.TaskListOptions) ([]swarm.Task, error) {
|
|
return TaskList, nil
|
|
},
|
|
NodeListF: func(context.Context, types.NodeListOptions) ([]swarm.Node, error) {
|
|
return NodeList, nil
|
|
},
|
|
}, nil
|
|
},
|
|
}
|
|
err := d.Gather(&acc)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestDockerGatherLabels(t *testing.T) {
|
|
var gatherLabelsTests = []struct {
|
|
include []string
|
|
exclude []string
|
|
expected []string
|
|
notexpected []string
|
|
}{
|
|
{[]string{}, []string{}, []string{"label1", "label2"}, []string{}},
|
|
{[]string{"*"}, []string{}, []string{"label1", "label2"}, []string{}},
|
|
{[]string{"lab*"}, []string{}, []string{"label1", "label2"}, []string{}},
|
|
{[]string{"label1"}, []string{}, []string{"label1"}, []string{"label2"}},
|
|
{[]string{"label1*"}, []string{}, []string{"label1"}, []string{"label2"}},
|
|
{[]string{}, []string{"*"}, []string{}, []string{"label1", "label2"}},
|
|
{[]string{}, []string{"lab*"}, []string{}, []string{"label1", "label2"}},
|
|
{[]string{}, []string{"label1"}, []string{"label2"}, []string{"label1"}},
|
|
{[]string{"*"}, []string{"*"}, []string{}, []string{"label1", "label2"}},
|
|
}
|
|
|
|
for _, tt := range gatherLabelsTests {
|
|
t.Run("", func(t *testing.T) {
|
|
var acc testutil.Accumulator
|
|
d := Docker{
|
|
newClient: newClient,
|
|
}
|
|
|
|
for _, label := range tt.include {
|
|
d.LabelInclude = append(d.LabelInclude, label)
|
|
}
|
|
for _, label := range tt.exclude {
|
|
d.LabelExclude = append(d.LabelExclude, label)
|
|
}
|
|
|
|
err := d.Gather(&acc)
|
|
require.NoError(t, err)
|
|
|
|
for _, label := range tt.expected {
|
|
if !acc.HasTag("docker_container_cpu", label) {
|
|
t.Errorf("Didn't get expected label of %s. Test was: Include: %s Exclude %s",
|
|
label, tt.include, tt.exclude)
|
|
}
|
|
}
|
|
|
|
for _, label := range tt.notexpected {
|
|
if acc.HasTag("docker_container_cpu", label) {
|
|
t.Errorf("Got unexpected label of %s. Test was: Include: %s Exclude %s",
|
|
label, tt.include, tt.exclude)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestContainerNames(t *testing.T) {
|
|
var gatherContainerNames = []struct {
|
|
include []string
|
|
exclude []string
|
|
expected []string
|
|
notexpected []string
|
|
}{
|
|
{[]string{}, []string{}, []string{"etcd", "etcd2"}, []string{}},
|
|
{[]string{"*"}, []string{}, []string{"etcd", "etcd2"}, []string{}},
|
|
{[]string{"etc*"}, []string{}, []string{"etcd", "etcd2"}, []string{}},
|
|
{[]string{"etcd"}, []string{}, []string{"etcd"}, []string{"etcd2"}},
|
|
{[]string{"etcd2*"}, []string{}, []string{"etcd2"}, []string{"etcd"}},
|
|
{[]string{}, []string{"etc*"}, []string{}, []string{"etcd", "etcd2"}},
|
|
{[]string{}, []string{"etcd"}, []string{"etcd2"}, []string{"etcd"}},
|
|
{[]string{"*"}, []string{"*"}, []string{"etcd", "etcd2"}, []string{}},
|
|
{[]string{}, []string{"*"}, []string{""}, []string{"etcd", "etcd2"}},
|
|
}
|
|
|
|
for _, tt := range gatherContainerNames {
|
|
t.Run("", func(t *testing.T) {
|
|
var acc testutil.Accumulator
|
|
|
|
d := Docker{
|
|
newClient: newClient,
|
|
ContainerInclude: tt.include,
|
|
ContainerExclude: tt.exclude,
|
|
}
|
|
|
|
err := d.Gather(&acc)
|
|
require.NoError(t, err)
|
|
|
|
for _, metric := range acc.Metrics {
|
|
if metric.Measurement == "docker_container_cpu" {
|
|
if val, ok := metric.Tags["container_name"]; ok {
|
|
var found bool = false
|
|
for _, cname := range tt.expected {
|
|
if val == cname {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("Got unexpected container of %s. Test was -> Include: %s, Exclude: %s", val, tt.include, tt.exclude)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, metric := range acc.Metrics {
|
|
if metric.Measurement == "docker_container_cpu" {
|
|
if val, ok := metric.Tags["container_name"]; ok {
|
|
var found bool = false
|
|
for _, cname := range tt.notexpected {
|
|
if val == cname {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if found {
|
|
t.Errorf("Got unexpected container of %s. Test was -> Include: %s, Exclude: %s", val, tt.include, tt.exclude)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDockerGatherInfo(t *testing.T) {
|
|
var acc testutil.Accumulator
|
|
d := Docker{
|
|
newClient: newClient,
|
|
TagEnvironment: []string{"ENVVAR1", "ENVVAR2", "ENVVAR3", "ENVVAR5",
|
|
"ENVVAR6", "ENVVAR7", "ENVVAR8", "ENVVAR9"},
|
|
}
|
|
|
|
err := acc.GatherError(d.Gather)
|
|
require.NoError(t, err)
|
|
|
|
acc.AssertContainsTaggedFields(t,
|
|
"docker",
|
|
map[string]interface{}{
|
|
"n_listener_events": int(0),
|
|
"n_cpus": int(4),
|
|
"n_used_file_descriptors": int(19),
|
|
"n_containers": int(108),
|
|
"n_containers_running": int(98),
|
|
"n_containers_stopped": int(6),
|
|
"n_containers_paused": int(3),
|
|
"n_images": int(199),
|
|
"n_goroutines": int(39),
|
|
},
|
|
map[string]string{"engine_host": "absol"},
|
|
)
|
|
|
|
acc.AssertContainsTaggedFields(t,
|
|
"docker_data",
|
|
map[string]interface{}{
|
|
"used": int64(17300000000),
|
|
"total": int64(107400000000),
|
|
"available": int64(36530000000),
|
|
},
|
|
map[string]string{
|
|
"unit": "bytes",
|
|
"engine_host": "absol",
|
|
},
|
|
)
|
|
acc.AssertContainsTaggedFields(t,
|
|
"docker_container_cpu",
|
|
map[string]interface{}{
|
|
"usage_total": uint64(1231652),
|
|
"container_id": "b7dfbb9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296e2173",
|
|
},
|
|
map[string]string{
|
|
"container_name": "etcd2",
|
|
"container_image": "quay.io:4443/coreos/etcd",
|
|
"cpu": "cpu3",
|
|
"container_version": "v2.2.2",
|
|
"engine_host": "absol",
|
|
"ENVVAR1": "loremipsum",
|
|
"ENVVAR2": "dolorsitamet",
|
|
"ENVVAR3": "=ubuntu:10.04",
|
|
"ENVVAR7": "ENVVAR8=ENVVAR9",
|
|
"label1": "test_value_1",
|
|
"label2": "test_value_2",
|
|
},
|
|
)
|
|
acc.AssertContainsTaggedFields(t,
|
|
"docker_container_mem",
|
|
map[string]interface{}{
|
|
"container_id": "b7dfbb9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296e2173",
|
|
"limit": uint64(18935443456),
|
|
"max_usage": uint64(0),
|
|
"usage": uint64(0),
|
|
"usage_percent": float64(0),
|
|
},
|
|
map[string]string{
|
|
"engine_host": "absol",
|
|
"container_name": "etcd2",
|
|
"container_image": "quay.io:4443/coreos/etcd",
|
|
"container_version": "v2.2.2",
|
|
"ENVVAR1": "loremipsum",
|
|
"ENVVAR2": "dolorsitamet",
|
|
"ENVVAR3": "=ubuntu:10.04",
|
|
"ENVVAR7": "ENVVAR8=ENVVAR9",
|
|
"label1": "test_value_1",
|
|
"label2": "test_value_2",
|
|
},
|
|
)
|
|
}
|
|
|
|
func TestDockerGatherSwarmInfo(t *testing.T) {
|
|
var acc testutil.Accumulator
|
|
d := Docker{
|
|
newClient: newClient,
|
|
}
|
|
|
|
err := acc.GatherError(d.Gather)
|
|
require.NoError(t, err)
|
|
|
|
d.gatherSwarmInfo(&acc)
|
|
|
|
// test docker_container_net measurement
|
|
acc.AssertContainsTaggedFields(t,
|
|
"docker_swarm",
|
|
map[string]interface{}{
|
|
"tasks_running": int(2),
|
|
"tasks_desired": uint64(2),
|
|
},
|
|
map[string]string{
|
|
"service_id": "qolkls9g5iasdiuihcyz9rnx2",
|
|
"service_name": "test1",
|
|
"service_mode": "replicated",
|
|
},
|
|
)
|
|
|
|
acc.AssertContainsTaggedFields(t,
|
|
"docker_swarm",
|
|
map[string]interface{}{
|
|
"tasks_running": int(1),
|
|
"tasks_desired": int(1),
|
|
},
|
|
map[string]string{
|
|
"service_id": "qolkls9g5iasdiuihcyz9rn3",
|
|
"service_name": "test2",
|
|
"service_mode": "global",
|
|
},
|
|
)
|
|
}
|