From 1550da7bbf0b65f26665d1d936f47c52aa532f29 Mon Sep 17 00:00:00 2001 From: Sven Rebhan Date: Tue, 28 Apr 2026 13:21:20 +0200 Subject: [PATCH 1/3] test(inputs.docker): Refactor unit-tests --- plugins/inputs/docker/docker_test.go | 2852 +++++++++-------- plugins/inputs/docker/docker_testdata.go | 589 ---- plugins/inputs/docker/testdata/disk.json | 66 + plugins/inputs/docker/testdata/info.json | 161 + plugins/inputs/docker/testdata/inspect.json | 73 + plugins/inputs/docker/testdata/list.json | 131 + plugins/inputs/docker/testdata/nodes.json | 38 + plugins/inputs/docker/testdata/services.json | 83 + .../docker/testdata/stats_123456789.json | 129 + ...8a1dd6f6d262bec172398cc10bc03c0d6841a.json | 106 + ...bbb753f0817192b5081334dc78476296e2173.json | 106 + ...5a0dab069319912221e5838a132ab18a8bc84.json | 106 + ...bbb753f0817192b5081334dc78476296b7dfb.json | 106 + ...5e047ab60ed5b2c4397c5a6b5bf40e1bd2791.json | 106 + .../testdata/stats_windows_123456789.json | 35 + plugins/inputs/docker/testdata/tasks.json | 59 + 16 files changed, 2804 insertions(+), 1942 deletions(-) delete mode 100644 plugins/inputs/docker/docker_testdata.go create mode 100644 plugins/inputs/docker/testdata/disk.json create mode 100644 plugins/inputs/docker/testdata/info.json create mode 100644 plugins/inputs/docker/testdata/inspect.json create mode 100644 plugins/inputs/docker/testdata/list.json create mode 100644 plugins/inputs/docker/testdata/nodes.json create mode 100644 plugins/inputs/docker/testdata/services.json create mode 100644 plugins/inputs/docker/testdata/stats_123456789.json create mode 100644 plugins/inputs/docker/testdata/stats_9bc6faf9ba8106fae32e8faafd38a1dd6f6d262bec172398cc10bc03c0d6841a.json create mode 100644 plugins/inputs/docker/testdata/stats_b7dfbb9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296e2173.json create mode 100644 plugins/inputs/docker/testdata/stats_d4ccced494a1d5fe8ebdb0a86335a0dab069319912221e5838a132ab18a8bc84.json create mode 100644 plugins/inputs/docker/testdata/stats_e2173b9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296b7dfb.json create mode 100644 plugins/inputs/docker/testdata/stats_e8a713dd90604f5a257b97c15945e047ab60ed5b2c4397c5a6b5bf40e1bd2791.json create mode 100644 plugins/inputs/docker/testdata/stats_windows_123456789.json create mode 100644 plugins/inputs/docker/testdata/tasks.json diff --git a/plugins/inputs/docker/docker_test.go b/plugins/inputs/docker/docker_test.go index 2caef3a714f9b..25d86c3cd93c0 100644 --- a/plugins/inputs/docker/docker_test.go +++ b/plugins/inputs/docker/docker_test.go @@ -1,11 +1,15 @@ package docker import ( + "bytes" "context" "crypto/tls" + "encoding/json" "errors" + "fmt" "io" - "reflect" + "os" + "path/filepath" "strings" "testing" "time" @@ -18,522 +22,641 @@ import ( "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/config" - "github.com/influxdata/telegraf/internal/choice" "github.com/influxdata/telegraf/metric" "github.com/influxdata/telegraf/models" "github.com/influxdata/telegraf/testutil" ) -type mockClient struct { - InfoF func() (system.Info, error) - ContainerListF func(options container.ListOptions) ([]container.Summary, error) - ContainerStatsF func(containerID string) (container.StatsResponseReader, error) - ContainerInspectF func() (container.InspectResponse, error) - ServiceListF func() ([]swarm.Service, error) - TaskListF func() ([]swarm.Task, error) - NodeListF func() ([]swarm.Node, error) - DiskUsageF func() (types.DiskUsage, error) - ClientVersionF func() string - PingF func() (types.Ping, error) - CloseF func() error -} - -func (c *mockClient) Info(context.Context) (system.Info, error) { - return c.InfoF() -} - -func (c *mockClient) ContainerList(_ context.Context, options container.ListOptions) ([]container.Summary, error) { - return c.ContainerListF(options) -} - -func (c *mockClient) ContainerStats(_ context.Context, containerID string, _ bool) (container.StatsResponseReader, error) { - return c.ContainerStatsF(containerID) -} - -func (c *mockClient) ContainerInspect(context.Context, string) (container.InspectResponse, error) { - return c.ContainerInspectF() -} - -func (c *mockClient) ServiceList(context.Context, swarm.ServiceListOptions) ([]swarm.Service, error) { - return c.ServiceListF() +func TestInit(t *testing.T) { + plugin := &Docker{ + Log: testutil.Logger{}, + PerDeviceInclude: []string{"cpu", "network", "blkio"}, + TotalInclude: []string{"cpu", "network", "blkio"}, + } + require.NoError(t, plugin.Init()) } -func (c *mockClient) TaskList(context.Context, swarm.TaskListOptions) ([]swarm.Task, error) { - return c.TaskListF() +func TestInitFail(t *testing.T) { + tests := []struct { + name string + perDevice []string + total []string + expected string + }{ + { + name: "unsupported perdevice_include", + perDevice: []string{"nonExistentClass"}, + total: []string{"cpu"}, + expected: "unknown choice nonExistentClass", + }, + { + name: "unsupported total_include", + perDevice: []string{"cpu"}, + total: []string{"nonExistentClass"}, + expected: "unknown choice nonExistentClass", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + plugin := &Docker{ + PerDeviceInclude: tt.perDevice, + TotalInclude: tt.total, + Log: testutil.Logger{}, + } + require.ErrorContains(t, plugin.Init(), tt.expected) + }) + } } -func (c *mockClient) NodeList(context.Context, swarm.NodeListOptions) ([]swarm.Node, error) { - return c.NodeListF() -} +func TestContainerStats(t *testing.T) { + // Load input data + data, err := readContainerData("testdata") + require.NoError(t, err) + stats := data.stats["123456789"] -func (c *mockClient) DiskUsage(context.Context, types.DiskUsageOptions) (types.DiskUsage, error) { - return c.DiskUsageF() -} + // Setup plugin + plugin := &Docker{ + PerDeviceInclude: containerMetricClasses, + TotalInclude: containerMetricClasses, + Log: testutil.Logger{}, + } -func (c *mockClient) ClientVersion() string { - return c.ClientVersionF() -} + // Collect the data + tags := map[string]string{ + "container_name": "redis", + "container_image": "redis/image", + } + var acc testutil.Accumulator + plugin.parseContainerStats(&stats, &acc, tags, "123456789", "linux") -func (c *mockClient) Ping(context.Context) (types.Ping, error) { - return c.PingF() + // Check the result + expected := []telegraf.Metric{ + metric.New( + "docker_container_net", + map[string]string{ + "container_name": "redis", + "container_image": "redis/image", + "network": "eth0", + }, + map[string]interface{}{ + "rx_dropped": uint64(1), + "rx_bytes": uint64(2), + "rx_errors": uint64(3), + "rx_packets": uint64(2), + "tx_packets": uint64(4), + "tx_dropped": uint64(1), + "tx_errors": uint64(3), + "tx_bytes": uint64(4), + "container_id": "123456789", + }, + time.Unix(0, 0), + ), + metric.New( + "docker_container_net", + map[string]string{ + "container_name": "redis", + "container_image": "redis/image", + "network": "eth1", + }, + map[string]interface{}{ + "rx_dropped": uint64(5), + "rx_bytes": uint64(6), + "rx_errors": uint64(7), + "rx_packets": uint64(6), + "tx_dropped": uint64(5), + "tx_errors": uint64(7), + "tx_bytes": uint64(8), + "tx_packets": uint64(8), + "container_id": "123456789", + }, + time.Unix(0, 0), + ), + metric.New( + "docker_container_net", + map[string]string{ + "container_name": "redis", + "container_image": "redis/image", + "network": "total", + }, + map[string]interface{}{ + "rx_dropped": uint64(6), + "rx_bytes": uint64(8), + "rx_errors": uint64(10), + "rx_packets": uint64(8), + "tx_packets": uint64(12), + "tx_dropped": uint64(6), + "tx_errors": uint64(10), + "tx_bytes": uint64(12), + "container_id": "123456789", + }, + time.Unix(0, 0), + ), + metric.New( + "docker_container_blkio", + map[string]string{ + "container_name": "redis", + "container_image": "redis/image", + "device": "6:0", + }, + map[string]interface{}{ + "io_service_bytes_recursive_read": uint64(100), + "io_serviced_recursive_write": uint64(101), + "container_id": "123456789", + }, + time.Unix(0, 0), + ), + metric.New( + "docker_container_blkio", + map[string]string{ + "container_name": "redis", + "container_image": "redis/image", + "device": "6:1", + }, + map[string]interface{}{ + "io_serviced_recursive_write": uint64(201), + "container_id": "123456789", + }, + time.Unix(0, 0), + ), + metric.New( + "docker_container_blkio", + map[string]string{ + "container_name": "redis", + "container_image": "redis/image", + "device": "total", + }, + map[string]interface{}{ + "io_service_bytes_recursive_read": uint64(100), + "io_serviced_recursive_write": uint64(302), + "container_id": "123456789", + }, + time.Unix(0, 0), + ), + metric.New( + "docker_container_mem", + map[string]string{ + "container_name": "redis", + "container_image": "redis/image", + }, + 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), + }, + time.Unix(0, 0), + ), + metric.New( + "docker_container_cpu", + map[string]string{ + "container_name": "redis", + "container_image": "redis/image", + "cpu": "cpu0", + }, + map[string]interface{}{ + "usage_total": uint64(1), + "container_id": "123456789", + }, + time.Unix(0, 0), + ), + metric.New( + "docker_container_cpu", + map[string]string{ + "container_name": "redis", + "container_image": "redis/image", + "cpu": "cpu1", + }, + map[string]interface{}{ + "usage_total": uint64(1002), + "container_id": "123456789", + }, + time.Unix(0, 0), + ), + metric.New( + "docker_container_cpu", + map[string]string{ + "container_name": "redis", + "container_image": "redis/image", + "cpu": "cpu-total", + }, + 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", + }, + time.Unix(0, 0), + ), + } + testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime(), testutil.SortMetrics()) } -func (c *mockClient) Close() error { - return c.CloseF() -} +func TestMemoryExcludesCache(t *testing.T) { + tests := []struct { + name string + override map[string]uint64 + expected []telegraf.Metric + }{ + { + name: "pre_19_03", + override: map[string]uint64{ + "cache": 16, + "total_inactive_file": 7, + "inactive_file": 9, + }, + expected: []telegraf.Metric{ + metric.New( + "docker_container_mem", + map[string]string{ + "container_name": "redis", + "container_image": "redis/image", + }, + map[string]interface{}{ + "active_anon": uint64(0), + "active_file": uint64(1), + "cache": uint64(16), + "container_id": "123456789", + "fail_count": uint64(1), + "hierarchical_memory_limit": uint64(0), + "inactive_anon": uint64(0), + "inactive_file": uint64(9), + "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(7), + "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(54.75), // 1095 / 2000 + "usage": uint64(1095), + "writeback": uint64(0), + }, + time.Unix(0, 0), + ), + }, + }, + { + name: "cgroup_v1", + override: map[string]uint64{ + "total_inactive_file": 7, + "inactive_file": 9, + }, + expected: []telegraf.Metric{ + metric.New( + "docker_container_mem", + map[string]string{ + "container_name": "redis", + "container_image": "redis/image", + }, + map[string]interface{}{ + "active_anon": uint64(0), + "active_file": uint64(1), + "container_id": "123456789", + "fail_count": uint64(1), + "hierarchical_memory_limit": uint64(0), + "inactive_anon": uint64(0), + "inactive_file": uint64(9), + "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(7), + "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.2), // 1104 / 2000 + "usage": uint64(1104), + "writeback": uint64(0), + }, + time.Unix(0, 0), + ), + }, + }, + { + name: "cgroup_v2", + override: map[string]uint64{ + "inactive_file": 9, + }, + expected: []telegraf.Metric{ + metric.New( + "docker_container_mem", + map[string]string{ + "container_name": "redis", + "container_image": "redis/image", + }, + map[string]interface{}{ + "active_anon": uint64(0), + "active_file": uint64(1), + "container_id": "123456789", + "fail_count": uint64(1), + "hierarchical_memory_limit": uint64(0), + "inactive_anon": uint64(0), + "inactive_file": uint64(9), + "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_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.1), // 1102 / 2000 + "usage": uint64(1102), + "writeback": uint64(0), + }, + time.Unix(0, 0), + ), + }, + }, + } -var baseClient = mockClient{ - InfoF: func() (system.Info, error) { - return info, nil - }, - ContainerListF: func(container.ListOptions) ([]container.Summary, error) { - return containerList, nil - }, - ContainerStatsF: func(s string) (container.StatsResponseReader, error) { - return containerStats(s), nil - }, - ContainerInspectF: func() (container.InspectResponse, error) { - return containerInspect(), nil - }, - ServiceListF: func() ([]swarm.Service, error) { - return serviceList, nil - }, - TaskListF: func() ([]swarm.Task, error) { - return taskList, nil - }, - NodeListF: func() ([]swarm.Node, error) { - return nodeList, nil - }, - DiskUsageF: func() (types.DiskUsage, error) { - return diskUsage, nil - }, - ClientVersionF: func() string { - return version - }, - PingF: func() (types.Ping, error) { - return types.Ping{}, nil - }, - CloseF: func() error { - return nil - }, -} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Load input data + data, err := readContainerData("testdata") + require.NoError(t, err) -func TestDockerGatherContainerStats(t *testing.T) { - var acc testutil.Accumulator - stats := testStats() + // Patch the statistic data + stats := data.stats["123456789"] + delete(stats.MemoryStats.Stats, "cache") + delete(stats.MemoryStats.Stats, "inactive_file") + delete(stats.MemoryStats.Stats, "total_inactive_file") + for k, v := range tt.override { + stats.MemoryStats.Stats[k] = v + } - tags := map[string]string{ - "container_name": "redis", - "container_image": "redis/image", - } + // Setup plugin + plugin := &Docker{ + Log: testutil.Logger{}, + } - d := &Docker{ - Log: testutil.Logger{}, - PerDeviceInclude: containerMetricClasses, - TotalInclude: containerMetricClasses, + // Collect the data and check the result + tags := map[string]string{ + "container_name": "redis", + "container_image": "redis/image", + } + var acc testutil.Accumulator + plugin.parseContainerStats(&stats, &acc, tags, "123456789", "linux") + testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime(), testutil.SortMetrics()) + }) } - d.parseContainerStats(stats, &acc, tags, "123456789", "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 TestDockerMemoryExcludesCache(t *testing.T) { - var acc testutil.Accumulator - stats := testStats() +func TestWindowsMemoryContainerStats(t *testing.T) { + // Setup client factory from data + factory := newFactoryFromFiles("testdata", true) - tags := map[string]string{ - "container_name": "redis", - "container_image": "redis/image", + // Setup the expected result + expected := []telegraf.Metric{ + metric.New( + "docker", + map[string]string{ + "engine_host": "absol", + "server_version": "17.09.0-ce", + }, + map[string]interface{}{ + "n_containers": int64(108), + "n_containers_paused": int64(3), + "n_containers_running": int64(98), + "n_containers_stopped": int64(6), + "n_cpus": int64(4), + "n_goroutines": int64(39), + "n_images": int64(199), + "n_listener_events": int64(0), + "n_used_file_descriptors": int64(19), + }, + time.Unix(0, 0), + ), + metric.New( + "docker", + map[string]string{ + "engine_host": "absol", + "server_version": "17.09.0-ce", + }, + map[string]interface{}{ + "memory_total": int64(3840757760), + }, + time.Unix(0, 0), + ), + metric.New( + "docker", + map[string]string{ + "engine_host": "absol", + "server_version": "17.09.0-ce", + "unit": "bytes", + }, + map[string]interface{}{ + "pool_blocksize": int64(65540), + }, + time.Unix(0, 0), + ), + metric.New( + "docker_data", + map[string]string{ + "engine_host": "absol", + "server_version": "17.09.0-ce", + "unit": "bytes", + }, + map[string]interface{}{ + "available": int64(36530000000), + "total": int64(107400000000), + "used": int64(17300000000), + }, + time.Unix(0, 0), + ), + metric.New( + "docker_metadata", + map[string]string{ + "engine_host": "absol", + "server_version": "17.09.0-ce", + "unit": "bytes", + }, + map[string]interface{}{ + "available": int64(2126999999), + "total": int64(2146999999), + "used": int64(20970000), + }, + time.Unix(0, 0), + ), + metric.New( + "docker_devicemapper", + map[string]string{ + "engine_host": "absol", + "server_version": "17.09.0-ce", + "pool_name": "docker-8:1-1182287-pool", + }, + map[string]interface{}{ + "base_device_size_bytes": int64(10740000000), + "data_space_available_bytes": int64(36530000000), + "data_space_total_bytes": int64(107400000000), + "data_space_used_bytes": int64(17300000000), + "metadata_space_available_bytes": int64(2126999999), + "metadata_space_total_bytes": int64(2146999999), + "metadata_space_used_bytes": int64(20970000), + "pool_blocksize_bytes": int64(65540), + "thin_pool_minimum_free_space_bytes": int64(10740000000), + }, + time.Unix(0, 0), + ), } - d := &Docker{ - Log: testutil.Logger{}, - } - - delete(stats.MemoryStats.Stats, "cache") - delete(stats.MemoryStats.Stats, "inactive_file") - delete(stats.MemoryStats.Stats, "total_inactive_file") - - // set cgroup v2 cache value - stats.MemoryStats.Stats["inactive_file"] = 9 - - d.parseContainerStats(stats, &acc, tags, "123456789", "linux") - - // test docker_container_mem measurement - memfields := map[string]interface{}{ - "active_anon": uint64(0), - "active_file": uint64(1), - "container_id": "123456789", - "fail_count": uint64(1), - "hierarchical_memory_limit": uint64(0), - "inactive_anon": uint64(0), - "inactive_file": uint64(9), - "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_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.1), // 1102 / 2000 - "usage": uint64(1102), - "writeback": uint64(0), - } - - acc.AssertContainsTaggedFields(t, "docker_container_mem", memfields, tags) - acc.ClearMetrics() - - // set cgroup v1 cache value (has priority over cgroups v2) - stats.MemoryStats.Stats["total_inactive_file"] = 7 - - d.parseContainerStats(stats, &acc, tags, "123456789", "linux") - - // 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(9), - "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(7), - "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.2), // 1104 / 2000 - "usage": uint64(1104), - "writeback": uint64(0), - } - - acc.AssertContainsTaggedFields(t, "docker_container_mem", memfields, tags) - acc.ClearMetrics() - - // set Docker 19.03 and older cache value (has priority over cgroups v1 and v2) - stats.MemoryStats.Stats["cache"] = 16 - - d.parseContainerStats(stats, &acc, tags, "123456789", "linux") - - // test docker_container_mem measurement - memfields = map[string]interface{}{ - "active_anon": uint64(0), - "active_file": uint64(1), - "cache": uint64(16), - "container_id": "123456789", - "fail_count": uint64(1), - "hierarchical_memory_limit": uint64(0), - "inactive_anon": uint64(0), - "inactive_file": uint64(9), - "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(7), - "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(54.75), // 1095 / 2000 - "usage": uint64(1095), - "writeback": uint64(0), - } - - acc.AssertContainsTaggedFields(t, "docker_container_mem", memfields, tags) -} + // Setup the plugin + plugin := &Docker{ + Timeout: config.Duration(5 * time.Second), + Log: testutil.Logger{}, + newClient: factory, + } + require.NoError(t, plugin.Init()) -func TestDocker_WindowsMemoryContainerStats(t *testing.T) { + // Start the plugin var acc testutil.Accumulator + require.NoError(t, plugin.Start(&acc)) + defer plugin.Stop() - d := Docker{ - Log: testutil.Logger{}, - Timeout: config.Duration(5 * time.Second), - newClient: func(string, *tls.Config) (dockerClient, error) { - return &mockClient{ - InfoF: func() (system.Info, error) { - return info, nil - }, - ContainerListF: func(container.ListOptions) ([]container.Summary, error) { - return containerList, nil - }, - ContainerStatsF: func(string) (container.StatsResponseReader, error) { - return containerStatsWindows(), nil - }, - ContainerInspectF: func() (container.InspectResponse, error) { - return containerInspect(), nil - }, - ServiceListF: func() ([]swarm.Service, error) { - return serviceList, nil - }, - TaskListF: func() ([]swarm.Task, error) { - return taskList, nil - }, - NodeListF: func() ([]swarm.Node, error) { - return nodeList, nil - }, - DiskUsageF: func() (types.DiskUsage, error) { - return diskUsage, nil - }, - ClientVersionF: func() string { - return version - }, - PingF: func() (types.Ping, error) { - return types.Ping{}, nil - }, - CloseF: func() error { - return nil - }, - }, nil - }, - } - require.NoError(t, d.Init()) - require.NoError(t, d.Start(&acc)) - err := d.Gather(&acc) - require.NoError(t, err) + // Collect data and test the result + require.NoError(t, plugin.Gather(&acc)) + testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime(), testutil.SortMetrics()) } func TestContainerLabels(t *testing.T) { var tests = []struct { - name string - container container.Summary - include []string - exclude []string - expected map[string]string + name string + labels map[string]string + include []string + exclude []string + expected map[string]string }{ { - name: "Nil filters matches all", - container: genContainerLabeled(map[string]string{ + name: "nil filters matches all", + labels: map[string]string{ "a": "x", - }), - include: nil, - exclude: nil, + }, expected: map[string]string{ "a": "x", }, }, { - name: "Empty filters matches all", - container: genContainerLabeled(map[string]string{ + name: "empty filters matches all", + labels: map[string]string{ "a": "x", - }), + }, expected: map[string]string{ "a": "x", }, }, { - name: "Must match include", - container: genContainerLabeled(map[string]string{ + name: "must match include", + labels: map[string]string{ "a": "x", "b": "y", - }), + }, include: []string{"a"}, expected: map[string]string{ "a": "x", }, }, { - name: "Must not match exclude", - container: genContainerLabeled(map[string]string{ + name: "must not match exclude", + labels: map[string]string{ "a": "x", "b": "y", - }), + }, exclude: []string{"b"}, expected: map[string]string{ "a": "x", }, }, { - name: "Include Glob", - container: genContainerLabeled(map[string]string{ + name: "include glob", + labels: map[string]string{ "aa": "x", "ab": "y", "bb": "z", - }), + }, include: []string{"a*"}, expected: map[string]string{ "aa": "x", @@ -541,24 +664,24 @@ func TestContainerLabels(t *testing.T) { }, }, { - name: "Exclude Glob", - container: genContainerLabeled(map[string]string{ + name: "exclude glob", + labels: map[string]string{ "aa": "x", "ab": "y", "bb": "z", - }), + }, exclude: []string{"a*"}, expected: map[string]string{ "bb": "z", }, }, { - name: "Excluded Includes", - container: genContainerLabeled(map[string]string{ + name: "excluded and includes", + labels: map[string]string{ "aa": "x", "ab": "y", "bb": "z", - }), + }, include: []string{"a*"}, exclude: []string{"*b"}, expected: map[string]string{ @@ -568,51 +691,45 @@ func TestContainerLabels(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - var acc testutil.Accumulator - - newClientFunc := func(string, *tls.Config) (dockerClient, error) { - client := baseClient - client.ContainerListF = func(container.ListOptions) ([]container.Summary, error) { - return []container.Summary{tt.container}, nil - } - return &client, nil + // Setup client factory and override container list data + data, err := readContainerData("testdata") + require.NoError(t, err) + c := data.summaries[0] + c.Labels = tt.labels + c.State = "running" + data.summaries = []container.Summary{c} + factory := func(string, *tls.Config) (dockerClient, error) { + return newClientFromData(data, false), nil } - d := Docker{ - Log: testutil.Logger{}, - newClient: newClientFunc, + // Setup plugin + plugin := &Docker{ LabelInclude: tt.include, LabelExclude: tt.exclude, TotalInclude: []string{"cpu"}, + Log: testutil.Logger{}, + newClient: factory, } + require.NoError(t, plugin.Init()) - require.NoError(t, d.Init()) - require.NoError(t, d.Start(&acc)) - err := d.Gather(&acc) - require.NoError(t, err) + var acc testutil.Accumulator + require.NoError(t, plugin.Start(&acc)) + defer plugin.Stop() - // Grab tags from a container metric + // Collect data and check result + require.NoError(t, acc.GatherError(plugin.Gather)) var actual map[string]string for _, mt := range acc.Metrics { if mt.Measurement == "docker_container_cpu" { actual = mt.Tags + break } } - - for k, v := range tt.expected { - require.Equal(t, v, actual[k]) - } + require.Subset(t, actual, tt.expected) }) } } -func genContainerLabeled(labels map[string]string) container.Summary { - c := containerList[0] - c.Labels = labels - c.State = "running" - return c -} - func TestContainerNames(t *testing.T) { var tests = []struct { name string @@ -622,58 +739,56 @@ func TestContainerNames(t *testing.T) { expected []string }{ { - name: "Nil filters matches all", - include: nil, - exclude: nil, + name: "nil filters matches all", expected: []string{"etcd", "etcd2", "acme", "acme-test", "foo"}, }, { - name: "Empty filters matches all", + name: "empty filters matches all", expected: []string{"etcd", "etcd2", "acme", "acme-test", "foo"}, }, { - name: "Match all containers", + name: "match all containers", include: []string{"*"}, expected: []string{"etcd", "etcd2", "acme", "acme-test", "foo"}, }, { - name: "Include prefix match", + name: "include prefix match", include: []string{"etc*"}, expected: []string{"etcd", "etcd2"}, }, { - name: "Exact match", + name: "exact match", include: []string{"etcd"}, expected: []string{"etcd"}, }, { - name: "Star matches zero length", + name: "star matches zero length", include: []string{"etcd2*"}, expected: []string{"etcd2"}, }, { - name: "Exclude matches all", + name: "exclude matches all", exclude: []string{"etc*"}, expected: []string{"acme", "acme-test", "foo"}, }, { - name: "Exclude single", + name: "exclude single", exclude: []string{"etcd"}, expected: []string{"etcd2", "acme", "acme-test", "foo"}, }, { - name: "Exclude all", + name: "exclude all", include: []string{"*"}, exclude: []string{"*"}, }, { - name: "Exclude item matching include", + name: "exclude item matching include", include: []string{"acme*"}, exclude: []string{"*test*"}, expected: []string{"acme"}, }, { - name: "Exclude item no wildcards", + name: "exclude item no wildcards", include: []string{"acme*"}, exclude: []string{"test"}, expected: []string{"acme", "acme-test"}, @@ -681,74 +796,46 @@ func TestContainerNames(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - var acc testutil.Accumulator - - newClientFunc := func(string, *tls.Config) (dockerClient, error) { - client := baseClient - client.ContainerListF = func(container.ListOptions) ([]container.Summary, error) { - return containerList, nil - } - client.ContainerStatsF = func(s string) (container.StatsResponseReader, error) { - return containerStats(s), nil - } + // Setup client factory + factory := newFactoryFromFiles("testdata", false) - return &client, nil - } - - d := Docker{ - Log: testutil.Logger{}, - newClient: newClientFunc, + // Setup plugin + plugin := &Docker{ ContainerInclude: tt.include, ContainerExclude: tt.exclude, + Log: testutil.Logger{}, + newClient: factory, } + require.NoError(t, plugin.Init()) - require.NoError(t, d.Init()) - require.NoError(t, d.Start(&acc)) - err := d.Gather(&acc) - require.NoError(t, err) - - // Set of expected names - expected := make(map[string]bool) - for _, v := range tt.expected { - expected[v] = true - } + var acc testutil.Accumulator + require.NoError(t, plugin.Start(&acc)) + defer plugin.Stop() - // Set of actual names - actual := make(map[string]bool) + // Collect data and check the results + require.NoError(t, acc.GatherError(plugin.Gather)) + actual := make([]string, 0) for _, mt := range acc.Metrics { if name, ok := mt.Tags["container_name"]; ok { - actual[name] = true + actual = append(actual, name) } } - - require.Equal(t, expected, actual) + require.Subset(t, tt.expected, actual) }) } } -func filterMetrics(metrics []telegraf.Metric, f func(telegraf.Metric) bool) []telegraf.Metric { - results := make([]telegraf.Metric, 0, len(metrics)) - for _, m := range metrics { - if f(m) { - results = append(results, m) - } - } - return results -} - func TestContainerStatus(t *testing.T) { var tests = []struct { name string - now func() time.Time - inspect container.InspectResponse + now time.Time + started *string + finished *string expected []telegraf.Metric }{ { name: "finished_at is zero value", - now: func() time.Time { - return time.Date(2018, 6, 14, 5, 51, 53, 266176036, time.UTC) - }, - inspect: containerInspect(), + now: time.Date(2018, 6, 14, 5, 51, 53, 266176036, time.UTC), expected: []telegraf.Metric{ metric.New( "docker_container_status", @@ -777,15 +864,9 @@ func TestContainerStatus(t *testing.T) { }, }, { - name: "finished_at is non-zero value", - now: func() time.Time { - return time.Date(2018, 6, 14, 5, 51, 53, 266176036, time.UTC) - }, - inspect: func() container.InspectResponse { - i := containerInspect() - i.ContainerJSONBase.State.FinishedAt = "2018-06-14T05:53:53.266176036Z" - return i - }(), + name: "finished_at is non-zero value", + now: time.Date(2018, 6, 14, 5, 51, 53, 266176036, time.UTC), + finished: new("2018-06-14T05:53:53.266176036Z"), expected: []telegraf.Metric{ metric.New( "docker_container_status", @@ -815,16 +896,10 @@ func TestContainerStatus(t *testing.T) { }, }, { - name: "started_at is zero value", - now: func() time.Time { - return time.Date(2018, 6, 14, 5, 51, 53, 266176036, time.UTC) - }, - inspect: func() container.InspectResponse { - i := containerInspect() - i.ContainerJSONBase.State.StartedAt = "" - i.ContainerJSONBase.State.FinishedAt = "2018-06-14T05:53:53.266176036Z" - return i - }(), + name: "started_at is zero value", + now: time.Date(2018, 6, 14, 5, 51, 53, 266176036, time.UTC), + started: new(""), + finished: new("2018-06-14T05:53:53.266176036Z"), expected: []telegraf.Metric{ metric.New( "docker_container_status", @@ -852,16 +927,10 @@ func TestContainerStatus(t *testing.T) { }, }, { - name: "container has been restarted", - now: func() time.Time { - return time.Date(2019, 1, 1, 0, 0, 3, 0, time.UTC) - }, - inspect: func() container.InspectResponse { - i := containerInspect() - i.ContainerJSONBase.State.StartedAt = "2019-01-01T00:00:02Z" - i.ContainerJSONBase.State.FinishedAt = "2019-01-01T00:00:01Z" - return i - }(), + name: "container has been restarted", + now: time.Date(2019, 1, 1, 0, 0, 3, 0, time.UTC), + started: new("2019-01-01T00:00:02Z"), + finished: new("2019-01-01T00:00:01Z"), expected: []telegraf.Metric{ metric.New( "docker_container_status", @@ -893,267 +962,284 @@ func TestContainerStatus(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - var ( - acc testutil.Accumulator - newClientFunc = func(string, *tls.Config) (dockerClient, error) { - client := baseClient - client.ContainerListF = func(container.ListOptions) ([]container.Summary, error) { - return containerList[:1], nil - } - client.ContainerInspectF = func() (container.InspectResponse, error) { - return tt.inspect, nil - } - - return &client, nil - } - d = Docker{ - Log: testutil.Logger{}, - newClient: newClientFunc, - IncludeSourceTag: true, - } - ) + // Mock the time + now = func() time.Time { return tt.now } + defer func() { now = time.Now }() + + // Setup client factory and override values with test-data + data, err := readContainerData("testdata") + require.NoError(t, err) + data.summaries = data.summaries[:1] + if tt.started != nil { + data.inspection.ContainerJSONBase.State.StartedAt = *tt.started + } + if tt.finished != nil { + data.inspection.ContainerJSONBase.State.FinishedAt = *tt.finished + } + factory := func(string, *tls.Config) (dockerClient, error) { + return newClientFromData(data, false), nil + } - // mock time - if tt.now != nil { - now = tt.now + // Setup plugin + plugin := &Docker{ + IncludeSourceTag: true, + Log: testutil.Logger{}, + newClient: factory, } - defer func() { - now = time.Now - }() + require.NoError(t, plugin.Init()) - require.NoError(t, d.Init()) - require.NoError(t, d.Start(&acc)) - err := d.Gather(&acc) - require.NoError(t, err) + var acc testutil.Accumulator + require.NoError(t, plugin.Start(&acc)) + defer plugin.Stop() - actual := filterMetrics(acc.GetTelegrafMetrics(), func(m telegraf.Metric) bool { - return m.Name() == "docker_container_status" - }) - testutil.RequireMetricsEqual(t, tt.expected, actual) + // Collect data and check the result + require.NoError(t, acc.GatherError(plugin.Gather)) + testutil.RequireMetricsSubset(t, tt.expected, acc.GetTelegrafMetrics()) }) } } -func TestDockerGatherInfo(t *testing.T) { - var acc testutil.Accumulator - d := Docker{ - Log: testutil.Logger{}, - newClient: func(string, *tls.Config) (dockerClient, error) { return &baseClient, nil }, +func TestGatherInfo(t *testing.T) { + // Setup client factory and override values with test-data + factory := newFactoryFromFiles("testdata", false) + + // Setup plugin + plugin := &Docker{ TagEnvironment: []string{"ENVVAR1", "ENVVAR2", "ENVVAR3", "ENVVAR5", "ENVVAR6", "ENVVAR7", "ENVVAR8", "ENVVAR9"}, PerDeviceInclude: []string{"cpu", "network", "blkio"}, TotalInclude: []string{"cpu", "blkio", "network"}, + Log: testutil.Logger{}, + newClient: factory, } + require.NoError(t, plugin.Init()) - require.NoError(t, d.Init()) - require.NoError(t, d.Start(&acc)) - 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", - "server_version": "17.09.0-ce", - }, - ) - - acc.AssertContainsTaggedFields(t, - "docker", - map[string]interface{}{ - "memory_total": int64(3840757760), - }, - map[string]string{ - "engine_host": "absol", - "server_version": "17.09.0-ce", - }, - ) + var acc testutil.Accumulator + require.NoError(t, plugin.Start(&acc)) + defer plugin.Stop() - acc.AssertContainsTaggedFields(t, - "docker", - map[string]interface{}{ - "pool_blocksize": int64(65540), - }, - map[string]string{ - "engine_host": "absol", - "server_version": "17.09.0-ce", - "unit": "bytes", - }, - ) - - acc.AssertContainsTaggedFields(t, - "docker_data", - map[string]interface{}{ - "used": int64(17300000000), - "total": int64(107400000000), - "available": int64(36530000000), - }, - map[string]string{ - "engine_host": "absol", - "server_version": "17.09.0-ce", - "unit": "bytes", - }, - ) - - acc.AssertContainsTaggedFields(t, - "docker_metadata", - map[string]interface{}{ - "used": int64(20970000), - "total": int64(2146999999), - "available": int64(2126999999), - }, - map[string]string{ - "engine_host": "absol", - "server_version": "17.09.0-ce", - "unit": "bytes", - }, - ) - - acc.AssertContainsTaggedFields(t, - "docker_devicemapper", - map[string]interface{}{ - "base_device_size_bytes": int64(10740000000), - "pool_blocksize_bytes": int64(65540), - "data_space_used_bytes": int64(17300000000), - "data_space_total_bytes": int64(107400000000), - "data_space_available_bytes": int64(36530000000), - "metadata_space_used_bytes": int64(20970000), - "metadata_space_total_bytes": int64(2146999999), - "metadata_space_available_bytes": int64(2126999999), - "thin_pool_minimum_free_space_bytes": int64(10740000000), - }, - map[string]string{ - "engine_host": "absol", - "server_version": "17.09.0-ce", - "pool_name": "docker-8:1-1182287-pool", - }, - ) + // Define expected result + expected := []telegraf.Metric{ + metric.New( + "docker", + map[string]string{ + "engine_host": "absol", + "server_version": "17.09.0-ce", + }, + 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), + }, + time.Unix(0, 0), + ), + metric.New( + "docker", + map[string]string{ + "engine_host": "absol", + "server_version": "17.09.0-ce", + }, + map[string]interface{}{ + "memory_total": int64(3840757760), + }, + time.Unix(0, 0), + ), + metric.New( + "docker", + map[string]string{ + "engine_host": "absol", + "server_version": "17.09.0-ce", + "unit": "bytes", + }, + map[string]interface{}{ + "pool_blocksize": int64(65540), + }, + time.Unix(0, 0), + ), + metric.New( + "docker_data", + map[string]string{ + "engine_host": "absol", + "server_version": "17.09.0-ce", + "unit": "bytes", + }, + map[string]interface{}{ + "used": int64(17300000000), + "total": int64(107400000000), + "available": int64(36530000000), + }, + time.Unix(0, 0), + ), + metric.New( + "docker_metadata", + map[string]string{ + "engine_host": "absol", + "server_version": "17.09.0-ce", + "unit": "bytes", + }, + map[string]interface{}{ + "used": int64(20970000), + "total": int64(2146999999), + "available": int64(2126999999), + }, + time.Unix(0, 0), + ), + metric.New( + "docker_devicemapper", + map[string]string{ + "engine_host": "absol", + "server_version": "17.09.0-ce", + "pool_name": "docker-8:1-1182287-pool", + }, + map[string]interface{}{ + "base_device_size_bytes": int64(10740000000), + "pool_blocksize_bytes": int64(65540), + "data_space_used_bytes": int64(17300000000), + "data_space_total_bytes": int64(107400000000), + "data_space_available_bytes": int64(36530000000), + "metadata_space_used_bytes": int64(20970000), + "metadata_space_total_bytes": int64(2146999999), + "metadata_space_available_bytes": int64(2126999999), + "thin_pool_minimum_free_space_bytes": int64(10740000000), + }, + time.Unix(0, 0), + ), + metric.New( + "docker_container_cpu", + map[string]string{ + "container_name": "etcd2", + "container_image": "quay.io:4443/coreos/etcd", + "cpu": "cpu3", + "container_version": "v3.3.25", + "engine_host": "absol", + "ENVVAR1": "loremipsum", + "ENVVAR2": "dolorsitamet", + "ENVVAR3": "=ubuntu:10.04", + "ENVVAR7": "ENVVAR8=ENVVAR9", + "label1": "test_value_1", + "label2": "test_value_2", + "server_version": "17.09.0-ce", + "container_status": "running", + }, + map[string]interface{}{ + "usage_total": uint64(1231652), + "container_id": "b7dfbb9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296e2173", + }, + time.Unix(0, 0), + ), + metric.New( + "docker_container_mem", + map[string]string{ + "engine_host": "absol", + "container_name": "etcd2", + "container_image": "quay.io:4443/coreos/etcd", + "container_version": "v3.3.25", + "ENVVAR1": "loremipsum", + "ENVVAR2": "dolorsitamet", + "ENVVAR3": "=ubuntu:10.04", + "ENVVAR7": "ENVVAR8=ENVVAR9", + "label1": "test_value_1", + "label2": "test_value_2", + "server_version": "17.09.0-ce", + "container_status": "running", + }, + map[string]interface{}{ + "container_id": "b7dfbb9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296e2173", + "limit": uint64(18935443456), + "max_usage": uint64(0), + "usage": uint64(0), + "usage_percent": float64(0), + }, + time.Unix(0, 0), + ), + } - 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": "v3.3.25", - "engine_host": "absol", - "ENVVAR1": "loremipsum", - "ENVVAR2": "dolorsitamet", - "ENVVAR3": "=ubuntu:10.04", - "ENVVAR7": "ENVVAR8=ENVVAR9", - "label1": "test_value_1", - "label2": "test_value_2", - "server_version": "17.09.0-ce", - "container_status": "running", - }, - ) - 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": "v3.3.25", - "ENVVAR1": "loremipsum", - "ENVVAR2": "dolorsitamet", - "ENVVAR3": "=ubuntu:10.04", - "ENVVAR7": "ENVVAR8=ENVVAR9", - "label1": "test_value_1", - "label2": "test_value_2", - "server_version": "17.09.0-ce", - "container_status": "running", - }, - ) + // Collect data and check the result + require.NoError(t, acc.GatherError(plugin.Gather)) + testutil.RequireMetricsSubset(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime(), testutil.SortMetrics()) } func TestDockerGatherSwarmInfo(t *testing.T) { - var acc testutil.Accumulator - d := Docker{ - Log: testutil.Logger{}, - newClient: func(string, *tls.Config) (dockerClient, error) { return &baseClient, nil }, - } - - require.NoError(t, d.Init()) - require.NoError(t, d.Start(&acc)) - err := acc.GatherError(d.Gather) - require.NoError(t, err) + // Setup client factory + factory := newFactoryFromFiles("testdata", false) - require.NoError(t, d.gatherSwarmInfo(&acc)) + // Setup plugin + plugin := &Docker{ + GatherServices: true, + Log: testutil.Logger{}, + newClient: factory, + } + require.NoError(t, plugin.Init()) - // 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", - }, - ) + var acc testutil.Accumulator + require.NoError(t, plugin.Start(&acc)) + defer plugin.Stop() - acc.AssertContainsTaggedFields(t, - "docker_swarm", - map[string]interface{}{ - "tasks_running": int(1), - "tasks_desired": uint64(1), - }, - map[string]string{ - "service_id": "qolkls9g5iasdiuihcyz9rn3", - "service_name": "test2", - "service_mode": "global", - }, - ) - - acc.AssertContainsTaggedFields(t, - "docker_swarm", - map[string]interface{}{ - "tasks_running": int(0), - "max_concurrent": uint64(2), - "total_completions": uint64(2), - }, - map[string]string{ - "service_id": "rfmqydhe8cluzl9hayyrhw5ga", - "service_name": "test3", - "service_mode": "replicated_job", - }, - ) + // Define the expected result + expected := []telegraf.Metric{ + metric.New( + "docker_swarm", + map[string]string{ + "service_id": "qolkls9g5iasdiuihcyz9rnx2", + "service_name": "test1", + "service_mode": "replicated", + }, + map[string]interface{}{ + "tasks_running": int(2), + "tasks_desired": uint64(2), + }, + time.Unix(0, 0), + ), + metric.New( + "docker_swarm", + map[string]string{ + "service_id": "qolkls9g5iasdiuihcyz9rn3", + "service_name": "test2", + "service_mode": "global", + }, + map[string]interface{}{ + "tasks_running": int(1), + "tasks_desired": uint64(1), + }, + time.Unix(0, 0), + ), + metric.New( + "docker_swarm", + map[string]string{ + "service_id": "rfmqydhe8cluzl9hayyrhw5ga", + "service_name": "test3", + "service_mode": "replicated_job", + }, + map[string]interface{}{ + "tasks_running": int(0), + "max_concurrent": uint64(2), + "total_completions": uint64(2), + }, + time.Unix(0, 0), + ), + metric.New( + "docker_swarm", + map[string]string{ + "service_id": "mp50lo68vqgkory4e26ts8f9d", + "service_name": "test4", + "service_mode": "global_job", + }, + map[string]interface{}{ + "tasks_running": int(0), + }, + time.Unix(0, 0), + ), + } - acc.AssertContainsTaggedFields(t, - "docker_swarm", - map[string]interface{}{ - "tasks_running": int(0), - }, - map[string]string{ - "service_id": "mp50lo68vqgkory4e26ts8f9d", - "service_name": "test4", - "service_mode": "global_job", - }, - ) + // Collect data and check the result + require.NoError(t, acc.GatherError(plugin.Gather)) + testutil.RequireMetricsSubset(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime(), testutil.SortMetrics()) } func TestContainerStateFilter(t *testing.T) { @@ -1195,84 +1281,95 @@ func TestContainerStateFilter(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var acc testutil.Accumulator + containerStates := []string{"created", "restarting", "running", "removing", "paused", "exited", "dead"} - containerStates := []string{"created", "restarting", "running", "removing", "paused", "exited", "dead"} - - newClientFunc := func(string, *tls.Config) (dockerClient, error) { - client := baseClient - client.ContainerListF = func(container.ListOptions) ([]container.Summary, error) { - containers := make([]container.Summary, 0, len(containerStates)) - for _, v := range containerStates { - containers = append(containers, container.Summary{ - Names: []string{v}, - State: v, - }) - } - return containers, nil - } - return &client, nil + t.Run(tt.name, func(t *testing.T) { + // Setup client factory + data, err := readContainerData("testdata") + require.NoError(t, err) + // Get an ID to use for gather to complete + var id string + for k := range data.stats { + id = k + break + } + // Fake states data + data.summaries = make([]container.Summary, 0, len(containerStates)) + for _, v := range containerStates { + data.summaries = append(data.summaries, container.Summary{ + ID: id, + Names: []string{v}, + State: v, + }) + } + factory := func(string, *tls.Config) (dockerClient, error) { + return newClientFromData(data, false), nil } - d := Docker{ - Log: testutil.Logger{}, - newClient: newClientFunc, + // Setup plugin + plugin := &Docker{ ContainerStateInclude: tt.include, ContainerStateExclude: tt.exclude, + Log: testutil.Logger{}, + newClient: factory, } + require.NoError(t, plugin.Init()) - require.NoError(t, d.Init()) - require.NoError(t, d.Start(&acc)) - err := d.Gather(&acc) - require.NoError(t, err) - - // Set of expected names - expected := make(map[string]bool) - for _, v := range tt.expected { - expected[v] = true - } + var acc testutil.Accumulator + require.NoError(t, plugin.Start(&acc)) + defer plugin.Stop() - // Set of actual names - actual := make(map[string]bool) + // Collect data and check the result + require.NoError(t, acc.GatherError(plugin.Gather), data.summaries) + actual := make([]string, 0, acc.NMetrics()) for _, mt := range acc.Metrics { if name, ok := mt.Tags["container_name"]; ok { - actual[name] = true + actual = append(actual, name) } } - - require.Equal(t, expected, actual) + require.Subset(t, actual, tt.expected) }) } } func TestContainerListRequestsAllContainers(t *testing.T) { - var gotOptions container.ListOptions - newClientFunc := func(string, *tls.Config) (dockerClient, error) { - client := baseClient + // Setup factory that records the container-list options + var actual container.ListOptions + factory := func(string, *tls.Config) (dockerClient, error) { + var client mockClient client.ContainerListF = func(options container.ListOptions) ([]container.Summary, error) { - gotOptions = options + actual = options return nil, nil } return &client, nil } - d := Docker{ + // Setup plugin + plugin := &Docker{ Log: testutil.Logger{}, - newClient: newClientFunc, + newClient: factory, } + require.NoError(t, plugin.Init()) var acc testutil.Accumulator - require.NoError(t, d.Init()) - require.NoError(t, d.Start(&acc)) - require.NoError(t, d.Gather(&acc)) + require.NoError(t, plugin.Start(&acc)) + defer plugin.Stop() - require.True(t, gotOptions.All, "ContainerList must request all containers so non-running states can be filtered client-side") + // Collect data and check that all containers are requested + require.NoError(t, acc.GatherError(plugin.Gather)) + require.True(t, actual.All, "ContainerList must request all containers so non-running states can be filtered client-side") } func TestNonRunningContainerEmitsStatusMetrics(t *testing.T) { - newClientFunc := func(string, *tls.Config) (dockerClient, error) { - client := baseClient + // Setup client factory + factory := func(string, *tls.Config) (dockerClient, error) { + var client mockClient + client.InfoF = func() (system.Info, error) { + return system.Info{ + Name: "absol", + ServerVersion: "17.09.0-ce", + }, nil + } client.ContainerListF = func(container.ListOptions) ([]container.Summary, error) { return []container.Summary{ { @@ -1303,18 +1400,50 @@ func TestNonRunningContainerEmitsStatusMetrics(t *testing.T) { return &client, nil } - d := Docker{ - Log: testutil.Logger{}, - newClient: newClientFunc, + // Setup client + plugin := &Docker{ ContainerStateInclude: []string{"exited"}, + Log: testutil.Logger{}, + newClient: factory, } + require.NoError(t, plugin.Init()) var acc testutil.Accumulator - require.NoError(t, d.Init()) - require.NoError(t, d.Start(&acc)) - require.NoError(t, d.Gather(&acc)) + require.NoError(t, plugin.Start(&acc)) + defer plugin.Stop() + // Define expected results expected := []telegraf.Metric{ + metric.New( + "docker", + map[string]string{ + "engine_host": "absol", + "server_version": "17.09.0-ce", + }, + map[string]interface{}{ + "n_containers": int64(0), + "n_containers_paused": int64(0), + "n_containers_running": int64(0), + "n_containers_stopped": int64(0), + "n_cpus": int64(0), + "n_images": int64(0), + "n_listener_events": int64(0), + "n_goroutines": int64(0), + "n_used_file_descriptors": int64(0), + }, + time.Unix(0, 0), + ), + metric.New( + "docker", + map[string]string{ + "engine_host": "absol", + "server_version": "17.09.0-ce", + }, + map[string]interface{}{ + "memory_total": int64(0), + }, + time.Unix(0, 0), + ), metric.New( "docker_container_status", map[string]string{ @@ -1335,531 +1464,307 @@ func TestNonRunningContainerEmitsStatusMetrics(t *testing.T) { "finished_at": time.Date(2024, 1, 1, 1, 0, 0, 0, time.UTC).UnixNano(), "uptime_ns": int64(time.Hour), }, - time.Time{}, + time.Unix(0, 0), ), } - actual := filterMetrics(acc.GetTelegrafMetrics(), func(m telegraf.Metric) bool { - return strings.HasPrefix(m.Name(), "docker_container_") - }) - testutil.RequireMetricsEqual(t, expected, actual, testutil.IgnoreTime()) -} - -func TestContainerName(t *testing.T) { - tests := []struct { - name string - clientFunc func(host string, tlsConfig *tls.Config) (dockerClient, error) - expected string - }{ - { - name: "container stats name is preferred", - clientFunc: func(string, *tls.Config) (dockerClient, error) { - client := baseClient - client.ContainerListF = func(container.ListOptions) ([]container.Summary, error) { - containers := []container.Summary{ - { - Names: []string{"/logspout/foo"}, - State: "running", - }, - } - return containers, nil - } - client.ContainerStatsF = func(string) (container.StatsResponseReader, error) { - return container.StatsResponseReader{ - Body: io.NopCloser(strings.NewReader(`{"name": "logspout"}`)), - }, nil - } - return &client, nil - }, - expected: "logspout", - }, - { - name: "container stats without name uses container list name", - clientFunc: func(string, *tls.Config) (dockerClient, error) { - client := baseClient - client.ContainerListF = func(container.ListOptions) ([]container.Summary, error) { - containers := []container.Summary{ - { - Names: []string{"/logspout"}, - State: "running", - }, - } - return containers, nil - } - client.ContainerStatsF = func(string) (container.StatsResponseReader, error) { - return container.StatsResponseReader{ - Body: io.NopCloser(strings.NewReader(`{}`)), - }, nil - } - return &client, nil - }, - expected: "logspout", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - d := Docker{ - Log: testutil.Logger{}, - newClient: tt.clientFunc, - } - var acc testutil.Accumulator - require.NoError(t, d.Init()) - require.NoError(t, d.Start(&acc)) - err := d.Gather(&acc) - require.NoError(t, err) - - for _, mt := range acc.Metrics { - // This tag is set on all container measurements - if mt.Measurement == "docker_container_mem" { - require.Equal(t, tt.expected, mt.Tags["container_name"]) - } - } - }) - } -} - -func TestHostnameFromID(t *testing.T) { - tests := []struct { - name string - id string - expect string - }{ - { - name: "Real ID", - id: "565e3a55f5843cfdd4aa5659a1a75e4e78d47f73c3c483f782fe4a26fc8caa07", - expect: "565e3a55f584", - }, - { - name: "Short ID", - id: "shortid123", - expect: "shortid123", - }, - { - name: "No ID", - id: "", - expect: "shortid123", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - output := hostnameFromID(test.id) - if test.expect != output { - t.Logf("Container ID for hostname is wrong. Want: %s, Got: %s", output, test.expect) - } - }) - } -} - -func Test_parseContainerStatsPerDeviceAndTotal(t *testing.T) { - type args struct { - stat *container.StatsResponse - tags map[string]string - id string - perDeviceInclude []string - totalInclude []string - daemonOSType string - } - - var ( - testDate = time.Date(2018, 6, 14, 5, 51, 53, 266176036, time.UTC) - metricCPUTotal = metric.New( - "docker_container_cpu", - map[string]string{ - "cpu": "cpu-total", - }, - map[string]interface{}{}, - testDate) - - metricCPU0 = metric.New( - "docker_container_cpu", - map[string]string{ - "cpu": "cpu0", - }, - map[string]interface{}{}, - testDate) - metricCPU1 = metric.New( - "docker_container_cpu", - map[string]string{ - "cpu": "cpu1", - }, - map[string]interface{}{}, - testDate) - - metricNetworkTotal = metric.New( - "docker_container_net", - map[string]string{ - "network": "total", - }, - map[string]interface{}{}, - testDate) - - metricNetworkEth0 = metric.New( - "docker_container_net", - map[string]string{ - "network": "eth0", - }, - map[string]interface{}{}, - testDate) - - metricNetworkEth1 = metric.New( - "docker_container_net", - map[string]string{ - "network": "eth0", - }, - map[string]interface{}{}, - testDate) - metricBlkioTotal = metric.New( - "docker_container_blkio", - map[string]string{ - "device": "total", - }, - map[string]interface{}{}, - testDate) - metricBlkio6_0 = metric.New( - "docker_container_blkio", - map[string]string{ - "device": "6:0", - }, - map[string]interface{}{}, - testDate) - metricBlkio6_1 = metric.New( - "docker_container_blkio", - map[string]string{ - "device": "6:1", - }, - map[string]interface{}{}, - testDate) - ) - stats := testStats() + // Collect and check results + require.NoError(t, acc.GatherError(plugin.Gather)) + testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime(), testutil.SortMetrics()) +} + +func TestContainerName(t *testing.T) { tests := []struct { - name string - args args - expected []telegraf.Metric + name string + containerNames []string + expected string }{ { - name: "Per device and total metrics enabled", - args: args{ - stat: stats, - perDeviceInclude: containerMetricClasses, - totalInclude: containerMetricClasses, - }, - expected: []telegraf.Metric{ - metricCPUTotal, metricCPU0, metricCPU1, - metricNetworkTotal, metricNetworkEth0, metricNetworkEth1, - metricBlkioTotal, metricBlkio6_0, metricBlkio6_1, - }, - }, - { - name: "Per device metrics enabled", - args: args{ - stat: stats, - perDeviceInclude: containerMetricClasses, - }, - expected: []telegraf.Metric{ - metricCPU0, metricCPU1, - metricNetworkEth0, metricNetworkEth1, - metricBlkio6_0, metricBlkio6_1, - }, - }, - { - name: "Total metrics enabled", - args: args{ - stat: stats, - totalInclude: containerMetricClasses, - }, - expected: []telegraf.Metric{metricCPUTotal, metricNetworkTotal, metricBlkioTotal}, + name: "container stats name is preferred", + containerNames: []string{"/logspout/foo"}, + expected: "logspout", }, { - name: "Per device and total metrics disabled", - args: args{ - stat: stats, - }, + name: "container stats without name uses container list name", + containerNames: []string{"/logspout"}, + expected: "logspout", }, } - for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - var acc testutil.Accumulator - d := &Docker{ - Log: testutil.Logger{}, - PerDeviceInclude: tt.args.perDeviceInclude, - TotalInclude: tt.args.totalInclude, + // Setup client factory + data, err := readContainerData("testdata") + require.NoError(t, err) + // Get an ID to use for gather to complete + var id string + for k := range data.stats { + id = k + break + } + // Fake the container list + data.summaries = []container.Summary{ + { + ID: id, + Names: []string{"/logspout"}, + State: "running", + }, } - d.parseContainerStats(tt.args.stat, &acc, tt.args.tags, tt.args.id, tt.args.daemonOSType) + factory := func(string, *tls.Config) (dockerClient, error) { + return newClientFromData(data, false), nil + } + + // Setup plugin + plugin := &Docker{ + Log: testutil.Logger{}, + newClient: factory, + } + require.NoError(t, plugin.Init()) - actual := filterMetrics(acc.GetTelegrafMetrics(), func(m telegraf.Metric) bool { - return choice.Contains(m.Name(), - []string{"docker_container_cpu", "docker_container_net", "docker_container_blkio"}) - }) - testutil.RequireMetricsEqual(t, tt.expected, actual, testutil.OnlyTags(), testutil.SortMetrics()) + var acc testutil.Accumulator + require.NoError(t, plugin.Start(&acc)) + defer plugin.Stop() + + // Collect data and check result + require.NoError(t, acc.GatherError(plugin.Gather)) + + for _, mt := range acc.Metrics { + // This tag is set on all container measurements + if mt.Measurement == "docker_container_mem" { + require.Equal(t, tt.expected, mt.Tags["container_name"]) + } + } }) } } -func TestDocker_Init(t *testing.T) { - type fields struct { - PerDeviceInclude []string - TotalInclude []string - } +func TestHostnameFromID(t *testing.T) { tests := []struct { - name string - fields fields - wantErr bool - wantPerDeviceInclude []string - wantTotalInclude []string + name string + id string + expected string }{ { - name: "Unsupported perdevice_include setting", - fields: fields{ - PerDeviceInclude: []string{"nonExistentClass"}, - TotalInclude: []string{"cpu"}, - }, - wantErr: true, + name: "Real ID", + id: "565e3a55f5843cfdd4aa5659a1a75e4e78d47f73c3c483f782fe4a26fc8caa07", + expected: "565e3a55f584", }, { - name: "Unsupported total_include setting", - fields: fields{ - PerDeviceInclude: []string{"cpu"}, - TotalInclude: []string{"nonExistentClass"}, - }, - wantErr: true, + name: "Short ID", + id: "shortid123", + expected: "shortid123", }, { - name: "Valid perdevice_include and total_include", - fields: fields{ - PerDeviceInclude: []string{"cpu", "network"}, - TotalInclude: []string{"cpu", "blkio"}, - }, - wantPerDeviceInclude: []string{"cpu", "network"}, - wantTotalInclude: []string{"cpu", "blkio"}, + name: "No ID", }, } + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - d := &Docker{ - Log: testutil.Logger{}, - PerDeviceInclude: tt.fields.PerDeviceInclude, - TotalInclude: tt.fields.TotalInclude, - } - err := d.Init() - if (err != nil) != tt.wantErr { - t.Errorf("Init() error = %v, wantErr %v", err, tt.wantErr) - } - - if err == nil { - if !reflect.DeepEqual(d.PerDeviceInclude, tt.wantPerDeviceInclude) { - t.Errorf("Perdevice include: got '%v', want '%v'", d.PerDeviceInclude, tt.wantPerDeviceInclude) - } - - if !reflect.DeepEqual(d.TotalInclude, tt.wantTotalInclude) { - t.Errorf("Total include: got '%v', want '%v'", d.TotalInclude, tt.wantTotalInclude) - } - } + require.Equal(t, tt.expected, hostnameFromID(tt.id)) }) } } -func TestDockerGatherDiskUsage(t *testing.T) { - var acc testutil.Accumulator - d := Docker{ - Log: testutil.Logger{}, - newClient: func(string, *tls.Config) (dockerClient, error) { return &baseClient, nil }, - } - - require.NoError(t, d.Init()) - require.NoError(t, d.Start(&acc)) +func TestGatherDiskUsage(t *testing.T) { + // Setup the client factory + factory := newFactoryFromFiles("testdata", false) - require.NoError(t, acc.GatherError(d.Gather)) + // Setup plugin + plugin := &Docker{ + StorageObjects: []string{"container"}, + Log: testutil.Logger{}, + newClient: factory, + } + require.NoError(t, plugin.Init()) - d.gatherDiskUsage(&acc, types.DiskUsageOptions{}) + var acc testutil.Accumulator + require.NoError(t, plugin.Start(&acc)) + defer plugin.Stop() - acc.AssertContainsTaggedFields(t, - "docker_disk_usage", - map[string]interface{}{ - "layers_size": int64(1e10), - }, - map[string]string{ - "engine_host": "absol", - "server_version": "17.09.0-ce", - }, - ) - - acc.AssertContainsTaggedFields(t, - "docker_disk_usage", - map[string]interface{}{ - "size_root_fs": int64(123456789), - "size_rw": int64(0)}, - map[string]string{ - "container_image": "some_image", - "container_version": "1.0.0-alpine", - "engine_host": "absol", - "server_version": "17.09.0-ce", - "container_name": "some_container", - }, - ) - - acc.AssertContainsTaggedFields(t, - "docker_disk_usage", - map[string]interface{}{ - "size": int64(123456789), - "shared_size": int64(0)}, - map[string]string{ - "image_id": "some_imageid", - "image_name": "some_image_tag", - "image_version": "1.0.0-alpine", - "engine_host": "absol", - "server_version": "17.09.0-ce", - }, - ) - - acc.AssertContainsTaggedFields(t, - "docker_disk_usage", - map[string]interface{}{ - "size": int64(425484494), - "shared_size": int64(0)}, - map[string]string{ - "image_id": "7f4a1cc74046", - "image_name": "telegraf", - "image_version": "latest", - "engine_host": "absol", - "server_version": "17.09.0-ce", - }, - ) + // Define the expected result + expected := []telegraf.Metric{ + metric.New( + "docker_disk_usage", + map[string]string{ + "engine_host": "absol", + "server_version": "17.09.0-ce", + }, + map[string]interface{}{ + "layers_size": int64(1e10), + }, + time.Unix(0, 0), + ), + metric.New( + "docker_disk_usage", + map[string]string{ + "container_image": "some_image", + "container_version": "1.0.0-alpine", + "engine_host": "absol", + "server_version": "17.09.0-ce", + "container_name": "some_container", + }, + map[string]interface{}{ + "size_root_fs": int64(123456789), + "size_rw": int64(0)}, + time.Unix(0, 0), + ), + metric.New( + "docker_disk_usage", + map[string]string{ + "image_id": "some_imageid", + "image_name": "some_image_tag", + "image_version": "1.0.0-alpine", + "engine_host": "absol", + "server_version": "17.09.0-ce", + }, + map[string]interface{}{ + "size": int64(123456789), + "shared_size": int64(0)}, + time.Unix(0, 0), + ), + metric.New( + "docker_disk_usage", + map[string]string{ + "image_id": "7f4a1cc74046", + "image_name": "telegraf", + "image_version": "latest", + "engine_host": "absol", + "server_version": "17.09.0-ce", + }, + map[string]interface{}{ + "size": int64(425484494), + "shared_size": int64(0)}, + time.Unix(0, 0), + ), + metric.New( + "docker_disk_usage", + map[string]string{ + "volume_name": "some_volume", + "engine_host": "absol", + "server_version": "17.09.0-ce", + }, + map[string]interface{}{ + "size": int64(123456789), + }, + time.Unix(0, 0), + ), + } - acc.AssertContainsTaggedFields(t, - "docker_disk_usage", - map[string]interface{}{ - "size": int64(123456789), - }, - map[string]string{ - "volume_name": "some_volume", - "engine_host": "absol", - "server_version": "17.09.0-ce", - }, - ) + // Collect data and check result + require.NoError(t, acc.GatherError(plugin.Gather)) + testutil.RequireMetricsSubset(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime(), testutil.SortMetrics()) } func TestPodmanDetection(t *testing.T) { tests := []struct { - name string - serverVersion string - engineName string - endpoint string - initBinary string - expectPodman bool + name string + version string + engine string + endpoint string + binary string + expected bool }{ { - name: "Docker engine", - serverVersion: "28.3.2", - engineName: "docker-desktop", - endpoint: "unix:///var/run/docker.sock", - initBinary: "docker-init", - expectPodman: false, + name: "Docker engine", + version: "28.3.2", + engine: "docker-desktop", + endpoint: "unix:///var/run/docker.sock", + binary: "docker-init", + expected: false, }, { - name: "Real Podman with version number", - serverVersion: "5.6.1", - engineName: "localhost.localdomain", - endpoint: "unix:///run/podman/podman.sock", - initBinary: "crun", - expectPodman: true, + name: "Real Podman with version number", + version: "5.6.1", + engine: "localhost.localdomain", + endpoint: "unix:///run/podman/podman.sock", + binary: "crun", + expected: true, }, { - name: "Podman with version string containing podman", - serverVersion: "4.9.4-podman", - engineName: "localhost", - endpoint: "unix:///run/podman/podman.sock", - expectPodman: true, + name: "Podman with version string containing podman", + version: "4.9.4-podman", + engine: "localhost", + endpoint: "unix:///run/podman/podman.sock", + expected: true, }, { - name: "Podman with podman in name", - serverVersion: "4.9.4", - engineName: "podman-machine", - endpoint: "unix:///var/run/docker.sock", - expectPodman: true, + name: "Podman with podman in name", + version: "4.9.4", + engine: "podman-machine", + endpoint: "unix:///var/run/docker.sock", + expected: true, }, { - name: "Podman detected by endpoint", - serverVersion: "5.2.0", - engineName: "localhost", - endpoint: "unix:///run/podman/podman.sock", - expectPodman: true, + name: "Podman detected by endpoint", + version: "5.2.0", + engine: "localhost", + endpoint: "unix:///run/podman/podman.sock", + expected: true, }, { - name: "Podman with crun runtime", - serverVersion: "5.0.1", - engineName: "myhost.local", - endpoint: "unix:///var/run/container.sock", - initBinary: "crun", - expectPodman: true, + name: "Podman with crun runtime", + version: "5.0.1", + engine: "myhost.local", + endpoint: "unix:///var/run/container.sock", + binary: "crun", + expected: true, }, { - name: "Docker with crun (should not detect as Podman)", - serverVersion: "20.10.7", - engineName: "docker-host", - endpoint: "unix:///var/run/docker.sock", - initBinary: "crun", - expectPodman: false, + name: "Docker with crun (should not detect as Podman)", + version: "20.10.7", + engine: "docker-host", + endpoint: "unix:///var/run/docker.sock", + binary: "crun", + expected: false, }, { - name: "Edge case - simple version with generic name", - serverVersion: "4.8.2", - engineName: "host", - endpoint: "unix:///var/run/container.sock", - expectPodman: true, + name: "Edge case - simple version with generic name", + version: "4.8.2", + engine: "host", + endpoint: "unix:///var/run/container.sock", + expected: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - var acc testutil.Accumulator - d := Docker{ - Endpoint: tt.endpoint, - Timeout: config.Duration(5 * time.Second), - newClient: func(string, *tls.Config) (dockerClient, error) { - return &mockClient{ - InfoF: func() (system.Info, error) { - return system.Info{ - Name: tt.engineName, - ServerVersion: tt.serverVersion, - InitBinary: tt.initBinary, - }, nil - }, - ContainerListF: func(container.ListOptions) ([]container.Summary, error) { - return nil, nil - }, - ServiceListF: func() ([]swarm.Service, error) { - return nil, nil - }, - ClientVersionF: func() string { - return "1.24.0" - }, - PingF: func() (types.Ping, error) { - return types.Ping{}, nil - }, - CloseF: func() error { - return nil - }, - }, nil - }, - Log: testutil.Logger{}, + // Setup client factory and override test-data + data, err := readContainerData("testdata") + require.NoError(t, err) + data.info = system.Info{ + Name: tt.engine, + ServerVersion: tt.version, + InitBinary: tt.binary, } + factory := func(string, *tls.Config) (dockerClient, error) { + return newClientFromData(data, false), nil + } + + // Setup plugin + plugin := &Docker{ + Endpoint: tt.endpoint, + Timeout: config.Duration(5 * time.Second), + Log: testutil.Logger{}, + newClient: factory, + } + require.NoError(t, plugin.Init()) - require.NoError(t, d.Init()) - require.NoError(t, d.Start(&acc)) - require.Equal(t, tt.expectPodman, d.isPodman, "Podman detection mismatch") + var acc testutil.Accumulator + require.NoError(t, plugin.Start(&acc)) + defer plugin.Stop() + + require.Equal(t, tt.expected, plugin.isPodman, "Podman detection mismatch") }) } } func TestPodmanStatsCache(t *testing.T) { // Create a mock Docker plugin configured as Podman - d := &Docker{ - isPodman: true, + plugin := &Docker{ PodmanCacheTTL: config.Duration(60 * time.Second), Log: testutil.Logger{}, statsCache: make(map[string]*cachedContainerStats), + isPodman: true, } // Create test stats @@ -1884,22 +1789,22 @@ func TestPodmanStatsCache(t *testing.T) { } // First call should cache the stats - d.fixPodmanCPUStats(testID, stats1) - require.Contains(t, d.statsCache, testID) - require.Equal(t, stats1, d.statsCache[testID].stats) + plugin.fixPodmanCPUStats(testID, stats1) + require.Contains(t, plugin.statsCache, testID) + require.Equal(t, stats1, plugin.statsCache[testID].stats) // Second call should use cached stats as PreCPUStats - d.fixPodmanCPUStats(testID, stats2) + plugin.fixPodmanCPUStats(testID, stats2) require.Equal(t, stats1.CPUStats, stats2.PreCPUStats) // Test cache cleanup - d.statsCache["old-container"] = &cachedContainerStats{ + plugin.statsCache["old-container"] = &cachedContainerStats{ stats: stats1, timestamp: time.Now().Add(-3 * time.Hour), } - d.cleanupStaleCache() - require.NotContains(t, d.statsCache, "old-container") - require.Contains(t, d.statsCache, testID) + plugin.cleanupStaleCache() + require.NotContains(t, plugin.statsCache, "old-container") + require.Contains(t, plugin.statsCache, testID) } func TestStartupErrorBehaviorError(t *testing.T) { @@ -1912,14 +1817,8 @@ func TestStartupErrorBehaviorError(t *testing.T) { PingF: func() (types.Ping, error) { return types.Ping{}, errors.New("connection refused") }, - CloseF: func() error { - return nil - }, }, nil }, - newEnvClient: func() (dockerClient, error) { - return nil, errors.New("not using env client") - }, } model := models.NewRunningInput(plugin, &models.InputConfig{ Name: "docker", @@ -1930,9 +1829,7 @@ func TestStartupErrorBehaviorError(t *testing.T) { // Starting the plugin will fail with an error because Ping fails var acc testutil.Accumulator - err := model.Start(&acc) - model.Stop() - require.ErrorContains(t, err, "failed to ping Docker daemon") + require.ErrorContains(t, model.Start(&acc), "failed to ping Docker daemon") } func TestStartupErrorBehaviorIgnore(t *testing.T) { @@ -1944,14 +1841,8 @@ func TestStartupErrorBehaviorIgnore(t *testing.T) { PingF: func() (types.Ping, error) { return types.Ping{}, errors.New("connection refused") }, - CloseF: func() error { - return nil - }, }, nil }, - newEnvClient: func() (dockerClient, error) { - return nil, errors.New("not using env client") - }, } model := models.NewRunningInput(plugin, &models.InputConfig{ Name: "docker", @@ -1963,9 +1854,7 @@ func TestStartupErrorBehaviorIgnore(t *testing.T) { // Starting the plugin will fail and model should convert to fatal error var acc testutil.Accumulator - err := model.Start(&acc) - model.Stop() - require.ErrorContains(t, err, "failed to ping Docker daemon") + require.ErrorContains(t, model.Start(&acc), "failed to ping Docker daemon") } func TestStartSuccess(t *testing.T) { @@ -1974,21 +1863,12 @@ func TestStartSuccess(t *testing.T) { Timeout: config.Duration(5 * time.Second), newClient: func(string, *tls.Config) (dockerClient, error) { return &mockClient{ - PingF: func() (types.Ping, error) { - return types.Ping{}, nil - }, InfoF: func() (system.Info, error) { return system.Info{ Name: "docker-desktop", ServerVersion: "20.10.0", }, nil }, - ClientVersionF: func() string { - return "1.24.0" - }, - CloseF: func() error { - return nil - }, }, nil }, newEnvClient: func() (dockerClient, error) { @@ -2006,3 +1886,269 @@ func TestStartSuccess(t *testing.T) { require.NoError(t, model.Start(&acc)) model.Stop() } + +// Internal + +type mockClient struct { + InfoF func() (system.Info, error) + ContainerListF func(options container.ListOptions) ([]container.Summary, error) + ContainerStatsF func(containerID string) (container.StatsResponseReader, error) + ContainerInspectF func() (container.InspectResponse, error) + ServiceListF func() ([]swarm.Service, error) + TaskListF func() ([]swarm.Task, error) + NodeListF func() ([]swarm.Node, error) + DiskUsageF func() (types.DiskUsage, error) + ClientVersionF func() string + PingF func() (types.Ping, error) + CloseF func() error +} + +func (c *mockClient) Info(context.Context) (system.Info, error) { + if c.InfoF == nil { + return system.Info{}, nil + } + return c.InfoF() +} + +func (c *mockClient) ContainerList(_ context.Context, options container.ListOptions) ([]container.Summary, error) { + if c.ContainerListF == nil { + return nil, errors.New("not implemented") + } + return c.ContainerListF(options) +} + +func (c *mockClient) ContainerStats(_ context.Context, containerID string, _ bool) (container.StatsResponseReader, error) { + if c.ContainerStatsF == nil { + return container.StatsResponseReader{}, errors.New("not implemented") + } + return c.ContainerStatsF(containerID) +} + +func (c *mockClient) ContainerInspect(context.Context, string) (container.InspectResponse, error) { + if c.ContainerInspectF == nil { + return container.InspectResponse{ + ContainerJSONBase: &container.ContainerJSONBase{}, + }, nil + } + return c.ContainerInspectF() +} + +func (c *mockClient) ServiceList(context.Context, swarm.ServiceListOptions) ([]swarm.Service, error) { + if c.ServiceListF == nil { + return nil, errors.New("not implemented") + } + return c.ServiceListF() +} + +func (c *mockClient) TaskList(context.Context, swarm.TaskListOptions) ([]swarm.Task, error) { + if c.TaskListF == nil { + return nil, errors.New("not implemented") + } + return c.TaskListF() +} + +func (c *mockClient) NodeList(context.Context, swarm.NodeListOptions) ([]swarm.Node, error) { + if c.NodeListF == nil { + return nil, errors.New("not implemented") + } + return c.NodeListF() +} + +func (c *mockClient) DiskUsage(context.Context, types.DiskUsageOptions) (types.DiskUsage, error) { + if c.DiskUsageF == nil { + return types.DiskUsage{}, errors.New("not implemented") + } + return c.DiskUsageF() +} + +func (c *mockClient) ClientVersion() string { + if c.ClientVersionF == nil { + return "1.43" + } + return c.ClientVersionF() +} + +func (c *mockClient) Ping(context.Context) (types.Ping, error) { + if c.PingF == nil { + return types.Ping{}, nil + } + return c.PingF() +} + +func (c *mockClient) Close() error { + if c.CloseF == nil { + return nil + } + return c.CloseF() +} + +type containerData struct { + info system.Info + summaries []container.Summary + stats map[string]container.StatsResponse + statsWindows map[string]container.StatsResponse + inspection container.InspectResponse + services []swarm.Service + tasks []swarm.Task + nodes []swarm.Node + disk types.DiskUsage +} + +func readContainerData(path string) (*containerData, error) { + var in containerData + + // Read info + if _, err := os.Stat(filepath.Join(path, "info.json")); err == nil { + buf, err := os.ReadFile(filepath.Join(path, "info.json")) + if err != nil { + return nil, fmt.Errorf("reading info failed: %w", err) + } + if err := json.Unmarshal(buf, &in.info); err != nil { + return nil, fmt.Errorf("parsing info failed: %w", err) + } + } + + // Read container list + if _, err := os.Stat(filepath.Join(path, "list.json")); err == nil { + buf, err := os.ReadFile(filepath.Join(path, "list.json")) + if err != nil { + return nil, fmt.Errorf("reading container list failed: %w", err) + } + if err := json.Unmarshal(buf, &in.summaries); err != nil { + return nil, fmt.Errorf("parsing container list failed: %w", err) + } + } + + // Read container statistics data + matches, err := filepath.Glob(filepath.Join(path, "stats_*.json")) + if err != nil { + return nil, fmt.Errorf("matching stats failed: %w", err) + } + in.stats = make(map[string]container.StatsResponse, len(matches)) + in.statsWindows = make(map[string]container.StatsResponse) + for _, fn := range matches { + buf, err := os.ReadFile(fn) + if err != nil { + return nil, fmt.Errorf("reading stats %q failed: %w", fn, err) + } + var stats container.StatsResponse + if err := json.Unmarshal(buf, &stats); err != nil { + return nil, fmt.Errorf("parsing stats %q failed: %w", fn, err) + } + id := strings.TrimSuffix(strings.TrimPrefix(filepath.Base(fn), "stats_"), ".json") + if strings.HasPrefix(id, "windows_") { + in.statsWindows[strings.TrimPrefix(id, "windows_")] = stats + } else { + in.stats[id] = stats + } + } + + // Read container inspection data + if _, err := os.Stat(filepath.Join(path, "inspect.json")); err == nil { + buf, err := os.ReadFile(filepath.Join(path, "inspect.json")) + if err != nil { + return nil, fmt.Errorf("reading inspection datafailed: %w", err) + } + if err := json.Unmarshal(buf, &in.inspection); err != nil { + return nil, fmt.Errorf("parsing inspection data failed: %w", err) + } + } + + // Read service data + if _, err := os.Stat(filepath.Join(path, "services.json")); err == nil { + buf, err := os.ReadFile(filepath.Join(path, "services.json")) + if err != nil { + return nil, fmt.Errorf("reading services failed: %w", err) + } + if err := json.Unmarshal(buf, &in.services); err != nil { + return nil, fmt.Errorf("parsing services failed: %w", err) + } + } + + // Read task data + if _, err := os.Stat(filepath.Join(path, "tasks.json")); err == nil { + buf, err := os.ReadFile(filepath.Join(path, "tasks.json")) + if err != nil { + return nil, fmt.Errorf("reading tasks failed: %w", err) + } + if err := json.Unmarshal(buf, &in.tasks); err != nil { + return nil, fmt.Errorf("parsing tasks failed: %w", err) + } + } + + // Read node data + if _, err := os.Stat(filepath.Join(path, "nodes.json")); err == nil { + buf, err := os.ReadFile(filepath.Join(path, "nodes.json")) + if err != nil { + return nil, fmt.Errorf("reading nodes failed: %w", err) + } + if err := json.Unmarshal(buf, &in.nodes); err != nil { + return nil, fmt.Errorf("parsing nodes failed: %w", err) + } + } + + // Read disk usage + if _, err := os.Stat(filepath.Join(path, "disk.json")); err == nil { + buf, err := os.ReadFile(filepath.Join(path, "disk.json")) + if err != nil { + return nil, fmt.Errorf("reading disk failed: %w", err) + } + if err := json.Unmarshal(buf, &in.disk); err != nil { + return nil, fmt.Errorf("parsing disk failed: %w", err) + } + } + + return &in, nil +} + +func newClientFromData(data *containerData, windows bool) *mockClient { + var stats map[string]container.StatsResponse + if windows { + stats = data.statsWindows + } else { + stats = data.stats + } + + return &mockClient{ + InfoF: func() (system.Info, error) { + return data.info, nil + }, + ContainerListF: func(container.ListOptions) ([]container.Summary, error) { + return data.summaries, nil + }, + ContainerStatsF: func(id string) (container.StatsResponseReader, error) { + s, found := stats[id] + if !found { + return container.StatsResponseReader{}, fmt.Errorf("stats for %q not found", id) + } + buf, err := json.Marshal(s) + if err != nil { + return container.StatsResponseReader{}, fmt.Errorf("encoding stats for %q failed: %w", id, err) + } + return container.StatsResponseReader{Body: io.NopCloser(bytes.NewReader(buf))}, nil + }, + ContainerInspectF: func() (container.InspectResponse, error) { + return data.inspection, nil + }, + ServiceListF: func() ([]swarm.Service, error) { + return data.services, nil + }, + TaskListF: func() ([]swarm.Task, error) { + return data.tasks, nil + }, + NodeListF: func() ([]swarm.Node, error) { + return data.nodes, nil + }, + DiskUsageF: func() (types.DiskUsage, error) { + return data.disk, nil + }, + } +} + +//nolint:unparam // For now 'path' is always 'testdata' but this will change in a follow-up PR +func newFactoryFromFiles(path string, windows bool) func(string, *tls.Config) (dockerClient, error) { + data, err := readContainerData(path) + return func(string, *tls.Config) (dockerClient, error) { + return newClientFromData(data, windows), err + } +} diff --git a/plugins/inputs/docker/docker_testdata.go b/plugins/inputs/docker/docker_testdata.go deleted file mode 100644 index 200dae5ad00e3..0000000000000 --- a/plugins/inputs/docker/docker_testdata.go +++ /dev/null @@ -1,589 +0,0 @@ -package docker - -import ( - "fmt" - "io" - "strings" - "time" - - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/image" - "github.com/docker/docker/api/types/registry" - "github.com/docker/docker/api/types/swarm" - "github.com/docker/docker/api/types/system" - "github.com/docker/docker/api/types/volume" -) - -var info = system.Info{ - Containers: 108, - ContainersRunning: 98, - ContainersStopped: 6, - ContainersPaused: 3, - OomKillDisable: false, - SystemTime: "2016-02-24T00:55:09.15073105-05:00", - NEventsListener: 0, - ID: "5WQQ:TFWR:FDNG:OKQ3:37Y4:FJWG:QIKK:623T:R3ME:QTKB:A7F7:OLHD", - Debug: false, - LoggingDriver: "json-file", - KernelVersion: "4.3.0-1-amd64", - IndexServerAddress: "https://index.docker.io/v1/", - MemTotal: 3840757760, - Images: 199, - CPUCfsQuota: true, - Name: "absol", - SwapLimit: false, - IPv4Forwarding: true, - ExperimentalBuild: false, - CPUCfsPeriod: true, - RegistryConfig: ®istry.ServiceConfig{ - IndexConfigs: map[string]*registry.IndexInfo{ - "docker.io": { - Name: "docker.io", - Mirrors: make([]string, 0), - Official: true, - Secure: true, - }, - }, InsecureRegistryCIDRs: []*registry.NetIPNet{{IP: []byte{127, 0, 0, 0}, Mask: []byte{255, 0, 0, 0}}}, Mirrors: make([]string, 0)}, - OperatingSystem: "Linux Mint LMDE (containerized)", - HTTPSProxy: "", - Labels: make([]string, 0), - MemoryLimit: false, - DriverStatus: [][2]string{ - {"Pool Name", "docker-8:1-1182287-pool"}, - {"Base Device Size", "10.74 GB"}, - {"Pool Blocksize", "65.54 kB"}, - {"Backing Filesystem", "extfs"}, - {"Data file", "/dev/loop0"}, - {"Metadata file", "/dev/loop1"}, - {"Data Space Used", "17.3 GB"}, - {"Data Space Total", "107.4 GB"}, - {"Data Space Available", "36.53 GB"}, - {"Metadata Space Used", "20.97 MB"}, - {"Metadata Space Total", "2.147 GB"}, - {"Metadata Space Available", "2.127 GB"}, - {"Udev Sync Supported", "true"}, - {"Deferred Removal Enabled", "false"}, - {"Data loop file", "/var/lib/docker/devicemapper/devicemapper/data"}, - {"Metadata loop file", "/var/lib/docker/devicemapper/devicemapper/metadata"}, - {"Library Version", "1.02.115 (2016-01-25)"}, - {"Thin Pool Minimum Free Space", "10.74GB"}, - }, - NFd: 19, - HTTPProxy: "", - Driver: "devicemapper", - NGoroutines: 39, - NCPU: 4, - DockerRootDir: "/var/lib/docker", - NoProxy: "", - ServerVersion: "17.09.0-ce", -} - -var containerList = []container.Summary{ - { - ID: "e2173b9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296b7dfb", - Names: []string{"/etcd"}, - Image: "quay.io/coreos/etcd:v3.3.25", - Command: "/etcd -name etcd0 -advertise-client-urls http://localhost:2379 -listen-client-urls http://0.0.0.0:2379", - Created: 1455941930, - State: "running", - Status: "Up 4 hours", - Ports: []container.Port{ - { - PrivatePort: 7001, - PublicPort: 0, - Type: "tcp", - }, - { - PrivatePort: 4001, - PublicPort: 0, - Type: "tcp", - }, - { - PrivatePort: 2380, - PublicPort: 0, - Type: "tcp", - }, - { - PrivatePort: 2379, - PublicPort: 2379, - Type: "tcp", - IP: "0.0.0.0", - }, - }, - Labels: map[string]string{ - "label1": "test_value_1", - "label2": "test_value_2", - }, - SizeRw: 0, - SizeRootFs: 0, - }, - { - ID: "b7dfbb9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296e2173", - Names: []string{"/etcd2"}, - Image: "quay.io:4443/coreos/etcd:v3.3.25", - Command: "/etcd -name etcd2 -advertise-client-urls http://localhost:2379 -listen-client-urls http://0.0.0.0:2379", - Created: 1455941933, - State: "running", - Status: "Up 4 hours", - Ports: []container.Port{ - { - PrivatePort: 7002, - PublicPort: 0, - Type: "tcp", - }, - { - PrivatePort: 4002, - PublicPort: 0, - Type: "tcp", - }, - { - PrivatePort: 2381, - PublicPort: 0, - Type: "tcp", - }, - { - PrivatePort: 2382, - PublicPort: 2382, - Type: "tcp", - IP: "0.0.0.0", - }, - }, - Labels: map[string]string{ - "label1": "test_value_1", - "label2": "test_value_2", - }, - SizeRw: 0, - SizeRootFs: 0, - }, - { - ID: "e8a713dd90604f5a257b97c15945e047ab60ed5b2c4397c5a6b5bf40e1bd2791", - Names: []string{"/acme"}, - State: "running", - }, - { - ID: "9bc6faf9ba8106fae32e8faafd38a1dd6f6d262bec172398cc10bc03c0d6841a", - Names: []string{"/acme-test"}, - State: "running", - }, - { - ID: "d4ccced494a1d5fe8ebdb0a86335a0dab069319912221e5838a132ab18a8bc84", - Names: []string{"/foo"}, - State: "running", - }, -} - -var two = uint64(2) -var serviceList = []swarm.Service{ - { - ID: "qolkls9g5iasdiuihcyz9rnx2", - Spec: swarm.ServiceSpec{ - Annotations: swarm.Annotations{ - Name: "test1", - }, - Mode: swarm.ServiceMode{ - Replicated: &swarm.ReplicatedService{ - Replicas: &two, - }, - }, - }, - }, - { - ID: "qolkls9g5iasdiuihcyz9rn3", - Spec: swarm.ServiceSpec{ - Annotations: swarm.Annotations{ - Name: "test2", - }, - Mode: swarm.ServiceMode{ - Global: &swarm.GlobalService{}, - }, - }, - }, - { - ID: "rfmqydhe8cluzl9hayyrhw5ga", - Spec: swarm.ServiceSpec{ - Annotations: swarm.Annotations{ - Name: "test3", - }, - Mode: swarm.ServiceMode{ - ReplicatedJob: &swarm.ReplicatedJob{ - MaxConcurrent: &two, - TotalCompletions: &two, - }, - }, - }, - }, - { - ID: "mp50lo68vqgkory4e26ts8f9d", - Spec: swarm.ServiceSpec{ - Annotations: swarm.Annotations{ - Name: "test4", - }, - Mode: swarm.ServiceMode{ - GlobalJob: &swarm.GlobalJob{}, - }, - }, - }, -} - -var taskList = []swarm.Task{ - { - ID: "kwh0lv7hwwbh", - ServiceID: "qolkls9g5iasdiuihcyz9rnx2", - NodeID: "0cl4jturcyd1ks3fwpd010kor", - Status: swarm.TaskStatus{ - State: "running", - }, - DesiredState: "running", - }, - { - ID: "u78m5ojbivc3", - ServiceID: "qolkls9g5iasdiuihcyz9rnx2", - NodeID: "0cl4jturcyd1ks3fwpd010kor", - Status: swarm.TaskStatus{ - State: "running", - }, - DesiredState: "running", - }, - { - ID: "1n1uilkhr98l", - ServiceID: "qolkls9g5iasdiuihcyz9rn3", - NodeID: "0cl4jturcyd1ks3fwpd010kor", - Status: swarm.TaskStatus{ - State: "running", - }, - DesiredState: "running", - }, -} - -var nodeList = []swarm.Node{ - { - ID: "0cl4jturcyd1ks3fwpd010kor", - Status: swarm.NodeStatus{ - State: "ready", - }, - }, - { - ID: "0cl4jturcyd1ks3fwpd010kor", - Status: swarm.NodeStatus{ - State: "ready", - }, - }, -} - -func containerStats(s string) container.StatsResponseReader { - var stat container.StatsResponseReader - var name string - switch s { - case "e2173b9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296b7dfb": - name = "etcd" - case "b7dfbb9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296e2173": - name = "etcd2" - case "e8a713dd90604f5a257b97c15945e047ab60ed5b2c4397c5a6b5bf40e1bd2791": - name = "/acme" - case "9bc6faf9ba8106fae32e8faafd38a1dd6f6d262bec172398cc10bc03c0d6841a": - name = "/acme-test" - case "d4ccced494a1d5fe8ebdb0a86335a0dab069319912221e5838a132ab18a8bc84": - name = "/foo" - } - - jsonStat := fmt.Sprintf(` -{ - "name": "%s", - "blkio_stats": { - "io_service_bytes_recursive": [ - { - "major": 252, - "minor": 1, - "op": "Read", - "value": 753664 - }, - { - "major": 252, - "minor": 1, - "op": "Write" - }, - { - "major": 252, - "minor": 1, - "op": "Sync" - }, - { - "major": 252, - "minor": 1, - "op": "Async", - "value": 753664 - }, - { - "major": 252, - "minor": 1, - "op": "Total", - "value": 753664 - } - ], - "io_serviced_recursive": [ - { - "major": 252, - "minor": 1, - "op": "Read", - "value": 26 - }, - { - "major": 252, - "minor": 1, - "op": "Write" - }, - { - "major": 252, - "minor": 1, - "op": "Sync" - }, - { - "major": 252, - "minor": 1, - "op": "Async", - "value": 26 - }, - { - "major": 252, - "minor": 1, - "op": "Total", - "value": 26 - } - ] - }, - "cpu_stats": { - "cpu_usage": { - "percpu_usage": [ - 17871, - 4959158, - 1646137, - 1231652, - 11829401, - 244656, - 369972, - 0 - ], - "total_usage": 20298847, - "usage_in_usermode": 10000000 - }, - "system_cpu_usage": 24052607520000000, - "throttling_data": {} - }, - "memory_stats": { - "limit": 18935443456, - "stats": {} - }, - "precpu_stats": { - "cpu_usage": { - "percpu_usage": [ - 17871, - 4959158, - 1646137, - 1231652, - 11829401, - 244656, - 369972, - 0 - ], - "total_usage": 20298847, - "usage_in_usermode": 10000000 - }, - "system_cpu_usage": 24052599550000000, - "throttling_data": {} - }, - "read": "2016-02-24T11:42:27.472459608-05:00" -}`, name) - stat.Body = io.NopCloser(strings.NewReader(jsonStat)) - return stat -} - -func testStats() *container.StatsResponse { - stats := &container.StatsResponse{} - stats.Read = time.Now() - stats.Networks = make(map[string]container.NetworkStats) - stats.CPUStats.OnlineCPUs = 2 - stats.CPUStats.CPUUsage.PercpuUsage = []uint64{1, 1002, 0, 0} - stats.CPUStats.CPUUsage.UsageInUsermode = 100 - stats.CPUStats.CPUUsage.TotalUsage = 500 - stats.CPUStats.CPUUsage.UsageInKernelmode = 200 - stats.CPUStats.SystemUsage = 100 - stats.CPUStats.ThrottlingData.Periods = 1 - - stats.PreCPUStats.CPUUsage.TotalUsage = 400 - stats.PreCPUStats.SystemUsage = 50 - - stats.MemoryStats.Stats = make(map[string]uint64) - stats.MemoryStats.Stats["active_anon"] = 0 - stats.MemoryStats.Stats["active_file"] = 1 - stats.MemoryStats.Stats["cache"] = 0 - stats.MemoryStats.Stats["hierarchical_memory_limit"] = 0 - stats.MemoryStats.Stats["inactive_anon"] = 0 - stats.MemoryStats.Stats["inactive_file"] = 3 - stats.MemoryStats.Stats["mapped_file"] = 0 - stats.MemoryStats.Stats["pgfault"] = 2 - stats.MemoryStats.Stats["pgmajfault"] = 0 - stats.MemoryStats.Stats["pgpgin"] = 0 - stats.MemoryStats.Stats["pgpgout"] = 0 - stats.MemoryStats.Stats["rss"] = 0 - stats.MemoryStats.Stats["rss_huge"] = 0 - stats.MemoryStats.Stats["total_active_anon"] = 0 - stats.MemoryStats.Stats["total_active_file"] = 0 - stats.MemoryStats.Stats["total_cache"] = 0 - stats.MemoryStats.Stats["total_inactive_anon"] = 0 - stats.MemoryStats.Stats["total_inactive_file"] = 0 - stats.MemoryStats.Stats["total_mapped_file"] = 0 - stats.MemoryStats.Stats["total_pgfault"] = 0 - stats.MemoryStats.Stats["total_pgmajfault"] = 0 - stats.MemoryStats.Stats["total_pgpgin"] = 4 - stats.MemoryStats.Stats["total_pgpgout"] = 0 - stats.MemoryStats.Stats["total_rss"] = 44 - stats.MemoryStats.Stats["total_rss_huge"] = 444 - stats.MemoryStats.Stats["total_unevictable"] = 0 - stats.MemoryStats.Stats["total_writeback"] = 55 - stats.MemoryStats.Stats["unevictable"] = 0 - stats.MemoryStats.Stats["writeback"] = 0 - - stats.MemoryStats.MaxUsage = 1001 - stats.MemoryStats.Usage = 1111 - stats.MemoryStats.Failcnt = 1 - stats.MemoryStats.Limit = 2000 - - stats.Networks["eth0"] = container.NetworkStats{ - RxDropped: 1, - RxBytes: 2, - RxErrors: 3, - TxPackets: 4, - TxDropped: 1, - RxPackets: 2, - TxErrors: 3, - TxBytes: 4, - } - - stats.Networks["eth1"] = container.NetworkStats{ - RxDropped: 5, - RxBytes: 6, - RxErrors: 7, - TxPackets: 8, - TxDropped: 5, - RxPackets: 6, - TxErrors: 7, - TxBytes: 8, - } - - sbr := container.BlkioStatEntry{ - Major: 6, - Minor: 0, - Op: "read", - Value: 100, - } - sr := container.BlkioStatEntry{ - Major: 6, - Minor: 0, - Op: "write", - Value: 101, - } - sr2 := container.BlkioStatEntry{ - Major: 6, - Minor: 1, - Op: "write", - Value: 201, - } - - stats.BlkioStats.IoServiceBytesRecursive = append( - stats.BlkioStats.IoServiceBytesRecursive, sbr) - stats.BlkioStats.IoServicedRecursive = append( - stats.BlkioStats.IoServicedRecursive, sr) - stats.BlkioStats.IoServicedRecursive = append( - stats.BlkioStats.IoServicedRecursive, sr2) - - return stats -} - -func containerStatsWindows() container.StatsResponseReader { - var stat container.StatsResponseReader - jsonStat := ` -{ - "read":"2017-01-11T08:32:46.2413794Z", - "preread":"0001-01-01T00:00:00Z", - "num_procs":64, - "cpu_stats":{ - "cpu_usage":{ - "total_usage":536718750, - "usage_in_kernelmode":390468750, - "usage_in_usermode":390468750 - }, - "throttling_data":{ - "periods":0, - "throttled_periods":0, - "throttled_time":0 - } - }, - "precpu_stats":{ - "cpu_usage":{ - "total_usage":0, - "usage_in_kernelmode":0, - "usage_in_usermode":0 - }, - "throttling_data":{ - "periods":0, - "throttled_periods":0, - "throttled_time":0 - } - }, - "memory_stats":{ - "commitbytes":77160448, - "commitpeakbytes":105000960, - "privateworkingset":59961344 - }, - "name":"/gt_test_iis", -}` - stat.Body = io.NopCloser(strings.NewReader(jsonStat)) - return stat -} - -func containerInspect() container.InspectResponse { - return container.InspectResponse{ - Config: &container.Config{ - Env: []string{ - "ENVVAR1=loremipsum", - "ENVVAR1FOO=loremipsum", - "ENVVAR2=dolorsitamet", - "ENVVAR3==ubuntu:10.04", - "ENVVAR4", - "ENVVAR5=", - "ENVVAR6= ", - "ENVVAR7=ENVVAR8=ENVVAR9", - "PATH=/bin:/sbin", - }, - }, - ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - Health: &container.Health{ - FailingStreak: 1, - Status: "Unhealthy", - }, - Status: "running", - OOMKilled: false, - Pid: 1234, - ExitCode: 0, - StartedAt: "2018-06-14T05:48:53.266176036Z", - FinishedAt: "0001-01-01T00:00:00Z", - }, - }, - } -} - -var diskUsage = types.DiskUsage{ - LayersSize: 1e10, - Containers: []*container.Summary{ - {Names: []string{"/some_container"}, Image: "some_image:1.0.0-alpine", SizeRw: 0, SizeRootFs: 123456789}, - }, - Images: []*image.Summary{ - {ID: "sha256:some_imageid", RepoTags: []string{"some_image_tag:1.0.0-alpine"}, Size: 123456789, SharedSize: 0}, - {ID: "sha256:7f4a1cc74046ce48cd918693cd6bf4b2683f4ce0d7be3f7148a21df9f06f5b5f", RepoTags: []string{"telegraf:latest"}, Size: 425484494, SharedSize: 0}, - }, - Volumes: []*volume.Volume{{Name: "some_volume", UsageData: &volume.UsageData{Size: 123456789}}}, -} - -var version = "1.43" diff --git a/plugins/inputs/docker/testdata/disk.json b/plugins/inputs/docker/testdata/disk.json new file mode 100644 index 0000000000000..2128127b8fd3b --- /dev/null +++ b/plugins/inputs/docker/testdata/disk.json @@ -0,0 +1,66 @@ +{ + "LayersSize": 10000000000, + "Images": [ + { + "Containers": 0, + "Created": 0, + "Id": "sha256:some_imageid", + "Labels": null, + "ParentId": "", + "RepoDigests": null, + "RepoTags": [ + "some_image_tag:1.0.0-alpine" + ], + "SharedSize": 0, + "Size": 123456789 + }, + { + "Containers": 0, + "Created": 0, + "Id": "sha256:7f4a1cc74046ce48cd918693cd6bf4b2683f4ce0d7be3f7148a21df9f06f5b5f", + "Labels": null, + "ParentId": "", + "RepoDigests": null, + "RepoTags": [ + "telegraf:latest" + ], + "SharedSize": 0, + "Size": 425484494 + } + ], + "Containers": [ + { + "Id": "", + "Names": [ + "/some_container" + ], + "Image": "some_image:1.0.0-alpine", + "ImageID": "", + "Command": "", + "Created": 0, + "Ports": null, + "SizeRootFs": 123456789, + "Labels": null, + "State": "", + "Status": "", + "HostConfig": {}, + "NetworkSettings": null, + "Mounts": null + } + ], + "Volumes": [ + { + "Driver": "", + "Labels": null, + "Mountpoint": "", + "Name": "some_volume", + "Options": null, + "Scope": "", + "UsageData": { + "RefCount": 0, + "Size": 123456789 + } + } + ], + "BuildCache": null +} \ No newline at end of file diff --git a/plugins/inputs/docker/testdata/info.json b/plugins/inputs/docker/testdata/info.json new file mode 100644 index 0000000000000..003ad1be1397f --- /dev/null +++ b/plugins/inputs/docker/testdata/info.json @@ -0,0 +1,161 @@ +{ + "ID": "5WQQ:TFWR:FDNG:OKQ3:37Y4:FJWG:QIKK:623T:R3ME:QTKB:A7F7:OLHD", + "Containers": 108, + "ContainersRunning": 98, + "ContainersPaused": 3, + "ContainersStopped": 6, + "Images": 199, + "Driver": "devicemapper", + "DriverStatus": [ + [ + "Pool Name", + "docker-8:1-1182287-pool" + ], + [ + "Base Device Size", + "10.74 GB" + ], + [ + "Pool Blocksize", + "65.54 kB" + ], + [ + "Backing Filesystem", + "extfs" + ], + [ + "Data file", + "/dev/loop0" + ], + [ + "Metadata file", + "/dev/loop1" + ], + [ + "Data Space Used", + "17.3 GB" + ], + [ + "Data Space Total", + "107.4 GB" + ], + [ + "Data Space Available", + "36.53 GB" + ], + [ + "Metadata Space Used", + "20.97 MB" + ], + [ + "Metadata Space Total", + "2.147 GB" + ], + [ + "Metadata Space Available", + "2.127 GB" + ], + [ + "Udev Sync Supported", + "true" + ], + [ + "Deferred Removal Enabled", + "false" + ], + [ + "Data loop file", + "/var/lib/docker/devicemapper/devicemapper/data" + ], + [ + "Metadata loop file", + "/var/lib/docker/devicemapper/devicemapper/metadata" + ], + [ + "Library Version", + "1.02.115 (2016-01-25)" + ], + [ + "Thin Pool Minimum Free Space", + "10.74GB" + ] + ], + "Plugins": { + "Volume": null, + "Network": null, + "Authorization": null, + "Log": null + }, + "MemoryLimit": false, + "SwapLimit": false, + "CpuCfsPeriod": true, + "CpuCfsQuota": true, + "CPUShares": false, + "CPUSet": false, + "PidsLimit": false, + "IPv4Forwarding": true, + "Debug": false, + "NFd": 19, + "OomKillDisable": false, + "NGoroutines": 39, + "SystemTime": "2016-02-24T00:55:09.15073105-05:00", + "LoggingDriver": "json-file", + "CgroupDriver": "", + "NEventsListener": 0, + "KernelVersion": "4.3.0-1-amd64", + "OperatingSystem": "Linux Mint LMDE (containerized)", + "OSVersion": "", + "OSType": "", + "Architecture": "", + "IndexServerAddress": "https://index.docker.io/v1/", + "RegistryConfig": { + "IndexConfigs": { + "docker.io": { + "Mirrors": [], + "Name": "docker.io", + "Official": true, + "Secure": true + } + }, + "InsecureRegistryCIDRs": [ + "127.0.0.0/8" + ], + "Mirrors": [] + }, + "NCPU": 4, + "MemTotal": 3840757760, + "GenericResources": null, + "DockerRootDir": "/var/lib/docker", + "HttpProxy": "", + "HttpsProxy": "", + "NoProxy": "", + "Name": "absol", + "Labels": [], + "ExperimentalBuild": false, + "ServerVersion": "17.09.0-ce", + "Runtimes": null, + "DefaultRuntime": "", + "Swarm": { + "NodeID": "", + "NodeAddr": "", + "LocalNodeState": "", + "ControlAvailable": false, + "Error": "", + "RemoteManagers": null + }, + "LiveRestoreEnabled": false, + "Isolation": "", + "InitBinary": "", + "ContainerdCommit": { + "ID": "" + }, + "RuncCommit": { + "ID": "" + }, + "InitCommit": { + "ID": "" + }, + "SecurityOptions": null, + "CDISpecDirs": null, + "Warnings": null +} \ No newline at end of file diff --git a/plugins/inputs/docker/testdata/inspect.json b/plugins/inputs/docker/testdata/inspect.json new file mode 100644 index 0000000000000..85701535a7ed3 --- /dev/null +++ b/plugins/inputs/docker/testdata/inspect.json @@ -0,0 +1,73 @@ +{ + "Id": "", + "Created": "", + "Path": "", + "Args": null, + "State": { + "Status": "running", + "Running": false, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 1234, + "ExitCode": 0, + "Error": "", + "StartedAt": "2018-06-14T05:48:53.266176036Z", + "FinishedAt": "0001-01-01T00:00:00Z", + "Health": { + "Status": "Unhealthy", + "FailingStreak": 1, + "Log": null + } + }, + "Image": "", + "ResolvConfPath": "", + "HostnamePath": "", + "HostsPath": "", + "LogPath": "", + "Name": "", + "RestartCount": 0, + "Driver": "", + "Platform": "", + "MountLabel": "", + "ProcessLabel": "", + "AppArmorProfile": "", + "ExecIDs": null, + "HostConfig": null, + "GraphDriver": { + "Data": null, + "Name": "" + }, + "Mounts": null, + "Config": { + "Hostname": "", + "Domainname": "", + "User": "", + "AttachStdin": false, + "AttachStdout": false, + "AttachStderr": false, + "Tty": false, + "OpenStdin": false, + "StdinOnce": false, + "Env": [ + "ENVVAR1=loremipsum", + "ENVVAR1FOO=loremipsum", + "ENVVAR2=dolorsitamet", + "ENVVAR3==ubuntu:10.04", + "ENVVAR4", + "ENVVAR5=", + "ENVVAR6= ", + "ENVVAR7=ENVVAR8=ENVVAR9", + "PATH=/bin:/sbin" + ], + "Cmd": null, + "Image": "", + "Volumes": null, + "WorkingDir": "", + "Entrypoint": null, + "OnBuild": null, + "Labels": null + }, + "NetworkSettings": null +} \ No newline at end of file diff --git a/plugins/inputs/docker/testdata/list.json b/plugins/inputs/docker/testdata/list.json new file mode 100644 index 0000000000000..a081b5a4d2f11 --- /dev/null +++ b/plugins/inputs/docker/testdata/list.json @@ -0,0 +1,131 @@ +[ + { + "Id": "e2173b9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296b7dfb", + "Names": [ + "/etcd" + ], + "Image": "quay.io/coreos/etcd:v3.3.25", + "ImageID": "", + "Command": "/etcd -name etcd0 -advertise-client-urls http://localhost:2379 -listen-client-urls http://0.0.0.0:2379", + "Created": 1455941930, + "Ports": [ + { + "PrivatePort": 7001, + "Type": "tcp" + }, + { + "PrivatePort": 4001, + "Type": "tcp" + }, + { + "PrivatePort": 2380, + "Type": "tcp" + }, + { + "IP": "0.0.0.0", + "PrivatePort": 2379, + "PublicPort": 2379, + "Type": "tcp" + } + ], + "Labels": { + "label1": "test_value_1", + "label2": "test_value_2" + }, + "State": "running", + "Status": "Up 4 hours", + "HostConfig": {}, + "NetworkSettings": null, + "Mounts": null + }, + { + "Id": "b7dfbb9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296e2173", + "Names": [ + "/etcd2" + ], + "Image": "quay.io:4443/coreos/etcd:v3.3.25", + "ImageID": "", + "Command": "/etcd -name etcd2 -advertise-client-urls http://localhost:2379 -listen-client-urls http://0.0.0.0:2379", + "Created": 1455941933, + "Ports": [ + { + "PrivatePort": 7002, + "Type": "tcp" + }, + { + "PrivatePort": 4002, + "Type": "tcp" + }, + { + "PrivatePort": 2381, + "Type": "tcp" + }, + { + "IP": "0.0.0.0", + "PrivatePort": 2382, + "PublicPort": 2382, + "Type": "tcp" + } + ], + "Labels": { + "label1": "test_value_1", + "label2": "test_value_2" + }, + "State": "running", + "Status": "Up 4 hours", + "HostConfig": {}, + "NetworkSettings": null, + "Mounts": null + }, + { + "Id": "e8a713dd90604f5a257b97c15945e047ab60ed5b2c4397c5a6b5bf40e1bd2791", + "Names": [ + "/acme" + ], + "Image": "", + "ImageID": "", + "Command": "", + "Created": 0, + "Ports": null, + "Labels": null, + "State": "running", + "Status": "", + "HostConfig": {}, + "NetworkSettings": null, + "Mounts": null + }, + { + "Id": "9bc6faf9ba8106fae32e8faafd38a1dd6f6d262bec172398cc10bc03c0d6841a", + "Names": [ + "/acme-test" + ], + "Image": "", + "ImageID": "", + "Command": "", + "Created": 0, + "Ports": null, + "Labels": null, + "State": "running", + "Status": "", + "HostConfig": {}, + "NetworkSettings": null, + "Mounts": null + }, + { + "Id": "d4ccced494a1d5fe8ebdb0a86335a0dab069319912221e5838a132ab18a8bc84", + "Names": [ + "/foo" + ], + "Image": "", + "ImageID": "", + "Command": "", + "Created": 0, + "Ports": null, + "Labels": null, + "State": "running", + "Status": "", + "HostConfig": {}, + "NetworkSettings": null, + "Mounts": null + } +] \ No newline at end of file diff --git a/plugins/inputs/docker/testdata/nodes.json b/plugins/inputs/docker/testdata/nodes.json new file mode 100644 index 0000000000000..dd7a397997114 --- /dev/null +++ b/plugins/inputs/docker/testdata/nodes.json @@ -0,0 +1,38 @@ +[ + { + "ID": "0cl4jturcyd1ks3fwpd010kor", + "Version": {}, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "Spec": { + "Labels": null + }, + "Description": { + "Platform": {}, + "Resources": {}, + "Engine": {}, + "TLSInfo": {} + }, + "Status": { + "State": "ready" + } + }, + { + "ID": "0cl4jturcyd1ks3fwpd010kor", + "Version": {}, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "Spec": { + "Labels": null + }, + "Description": { + "Platform": {}, + "Resources": {}, + "Engine": {}, + "TLSInfo": {} + }, + "Status": { + "State": "ready" + } + } +] \ No newline at end of file diff --git a/plugins/inputs/docker/testdata/services.json b/plugins/inputs/docker/testdata/services.json new file mode 100644 index 0000000000000..c6684612b372e --- /dev/null +++ b/plugins/inputs/docker/testdata/services.json @@ -0,0 +1,83 @@ +[ + { + "ID": "qolkls9g5iasdiuihcyz9rnx2", + "Version": {}, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "Spec": { + "Name": "test1", + "Labels": null, + "TaskTemplate": { + "ForceUpdate": 0 + }, + "Mode": { + "Replicated": { + "Replicas": 2 + } + } + }, + "Endpoint": { + "Spec": {} + } + }, + { + "ID": "qolkls9g5iasdiuihcyz9rn3", + "Version": {}, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "Spec": { + "Name": "test2", + "Labels": null, + "TaskTemplate": { + "ForceUpdate": 0 + }, + "Mode": { + "Global": {} + } + }, + "Endpoint": { + "Spec": {} + } + }, + { + "ID": "rfmqydhe8cluzl9hayyrhw5ga", + "Version": {}, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "Spec": { + "Name": "test3", + "Labels": null, + "TaskTemplate": { + "ForceUpdate": 0 + }, + "Mode": { + "ReplicatedJob": { + "MaxConcurrent": 2, + "TotalCompletions": 2 + } + } + }, + "Endpoint": { + "Spec": {} + } + }, + { + "ID": "mp50lo68vqgkory4e26ts8f9d", + "Version": {}, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "Spec": { + "Name": "test4", + "Labels": null, + "TaskTemplate": { + "ForceUpdate": 0 + }, + "Mode": { + "GlobalJob": {} + } + }, + "Endpoint": { + "Spec": {} + } + } +] \ No newline at end of file diff --git a/plugins/inputs/docker/testdata/stats_123456789.json b/plugins/inputs/docker/testdata/stats_123456789.json new file mode 100644 index 0000000000000..6e5b434e63094 --- /dev/null +++ b/plugins/inputs/docker/testdata/stats_123456789.json @@ -0,0 +1,129 @@ +{ + "read": "2026-04-27T15:49:54.493486224+02:00", + "preread": "0001-01-01T00:00:00Z", + "pids_stats": {}, + "blkio_stats": { + "io_service_bytes_recursive": [ + { + "major": 6, + "minor": 0, + "op": "read", + "value": 100 + } + ], + "io_serviced_recursive": [ + { + "major": 6, + "minor": 0, + "op": "write", + "value": 101 + }, + { + "major": 6, + "minor": 1, + "op": "write", + "value": 201 + } + ], + "io_queue_recursive": null, + "io_service_time_recursive": null, + "io_wait_time_recursive": null, + "io_merged_recursive": null, + "io_time_recursive": null, + "sectors_recursive": null + }, + "num_procs": 0, + "storage_stats": {}, + "cpu_stats": { + "cpu_usage": { + "total_usage": 500, + "percpu_usage": [ + 1, + 1002, + 0, + 0 + ], + "usage_in_kernelmode": 200, + "usage_in_usermode": 100 + }, + "system_cpu_usage": 100, + "online_cpus": 2, + "throttling_data": { + "periods": 1, + "throttled_periods": 0, + "throttled_time": 0 + } + }, + "precpu_stats": { + "cpu_usage": { + "total_usage": 400, + "usage_in_kernelmode": 0, + "usage_in_usermode": 0 + }, + "system_cpu_usage": 50, + "throttling_data": { + "periods": 0, + "throttled_periods": 0, + "throttled_time": 0 + } + }, + "memory_stats": { + "usage": 1111, + "max_usage": 1001, + "stats": { + "active_anon": 0, + "active_file": 1, + "cache": 0, + "hierarchical_memory_limit": 0, + "inactive_anon": 0, + "inactive_file": 3, + "mapped_file": 0, + "pgfault": 2, + "pgmajfault": 0, + "pgpgin": 0, + "pgpgout": 0, + "rss": 0, + "rss_huge": 0, + "total_active_anon": 0, + "total_active_file": 0, + "total_cache": 0, + "total_inactive_anon": 0, + "total_inactive_file": 0, + "total_mapped_file": 0, + "total_pgfault": 0, + "total_pgmajfault": 0, + "total_pgpgin": 4, + "total_pgpgout": 0, + "total_rss": 44, + "total_rss_huge": 444, + "total_unevictable": 0, + "total_writeback": 55, + "unevictable": 0, + "writeback": 0 + }, + "failcnt": 1, + "limit": 2000 + }, + "networks": { + "eth0": { + "rx_bytes": 2, + "rx_packets": 2, + "rx_errors": 3, + "rx_dropped": 1, + "tx_bytes": 4, + "tx_packets": 4, + "tx_errors": 3, + "tx_dropped": 1 + }, + "eth1": { + "rx_bytes": 6, + "rx_packets": 6, + "rx_errors": 7, + "rx_dropped": 5, + "tx_bytes": 8, + "tx_packets": 8, + "tx_errors": 7, + "tx_dropped": 5 + } + } +} \ No newline at end of file diff --git a/plugins/inputs/docker/testdata/stats_9bc6faf9ba8106fae32e8faafd38a1dd6f6d262bec172398cc10bc03c0d6841a.json b/plugins/inputs/docker/testdata/stats_9bc6faf9ba8106fae32e8faafd38a1dd6f6d262bec172398cc10bc03c0d6841a.json new file mode 100644 index 0000000000000..ff7a49b1f8888 --- /dev/null +++ b/plugins/inputs/docker/testdata/stats_9bc6faf9ba8106fae32e8faafd38a1dd6f6d262bec172398cc10bc03c0d6841a.json @@ -0,0 +1,106 @@ +{ + "name": "/acme-test", + "blkio_stats": { + "io_service_bytes_recursive": [ + { + "major": 252, + "minor": 1, + "op": "Read", + "value": 753664 + }, + { + "major": 252, + "minor": 1, + "op": "Write" + }, + { + "major": 252, + "minor": 1, + "op": "Sync" + }, + { + "major": 252, + "minor": 1, + "op": "Async", + "value": 753664 + }, + { + "major": 252, + "minor": 1, + "op": "Total", + "value": 753664 + } + ], + "io_serviced_recursive": [ + { + "major": 252, + "minor": 1, + "op": "Read", + "value": 26 + }, + { + "major": 252, + "minor": 1, + "op": "Write" + }, + { + "major": 252, + "minor": 1, + "op": "Sync" + }, + { + "major": 252, + "minor": 1, + "op": "Async", + "value": 26 + }, + { + "major": 252, + "minor": 1, + "op": "Total", + "value": 26 + } + ] + }, + "cpu_stats": { + "cpu_usage": { + "percpu_usage": [ + 17871, + 4959158, + 1646137, + 1231652, + 11829401, + 244656, + 369972, + 0 + ], + "total_usage": 20298847, + "usage_in_usermode": 10000000 + }, + "system_cpu_usage": 24052607520000000, + "throttling_data": {} + }, + "memory_stats": { + "limit": 18935443456, + "stats": {} + }, + "precpu_stats": { + "cpu_usage": { + "percpu_usage": [ + 17871, + 4959158, + 1646137, + 1231652, + 11829401, + 244656, + 369972, + 0 + ], + "total_usage": 20298847, + "usage_in_usermode": 10000000 + }, + "system_cpu_usage": 24052599550000000, + "throttling_data": {} + }, + "read": "2016-02-24T11:42:27.472459608-05:00" +} diff --git a/plugins/inputs/docker/testdata/stats_b7dfbb9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296e2173.json b/plugins/inputs/docker/testdata/stats_b7dfbb9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296e2173.json new file mode 100644 index 0000000000000..006b8cba69e20 --- /dev/null +++ b/plugins/inputs/docker/testdata/stats_b7dfbb9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296e2173.json @@ -0,0 +1,106 @@ +{ + "name": "etcd2", + "blkio_stats": { + "io_service_bytes_recursive": [ + { + "major": 252, + "minor": 1, + "op": "Read", + "value": 753664 + }, + { + "major": 252, + "minor": 1, + "op": "Write" + }, + { + "major": 252, + "minor": 1, + "op": "Sync" + }, + { + "major": 252, + "minor": 1, + "op": "Async", + "value": 753664 + }, + { + "major": 252, + "minor": 1, + "op": "Total", + "value": 753664 + } + ], + "io_serviced_recursive": [ + { + "major": 252, + "minor": 1, + "op": "Read", + "value": 26 + }, + { + "major": 252, + "minor": 1, + "op": "Write" + }, + { + "major": 252, + "minor": 1, + "op": "Sync" + }, + { + "major": 252, + "minor": 1, + "op": "Async", + "value": 26 + }, + { + "major": 252, + "minor": 1, + "op": "Total", + "value": 26 + } + ] + }, + "cpu_stats": { + "cpu_usage": { + "percpu_usage": [ + 17871, + 4959158, + 1646137, + 1231652, + 11829401, + 244656, + 369972, + 0 + ], + "total_usage": 20298847, + "usage_in_usermode": 10000000 + }, + "system_cpu_usage": 24052607520000000, + "throttling_data": {} + }, + "memory_stats": { + "limit": 18935443456, + "stats": {} + }, + "precpu_stats": { + "cpu_usage": { + "percpu_usage": [ + 17871, + 4959158, + 1646137, + 1231652, + 11829401, + 244656, + 369972, + 0 + ], + "total_usage": 20298847, + "usage_in_usermode": 10000000 + }, + "system_cpu_usage": 24052599550000000, + "throttling_data": {} + }, + "read": "2016-02-24T11:42:27.472459608-05:00" +} diff --git a/plugins/inputs/docker/testdata/stats_d4ccced494a1d5fe8ebdb0a86335a0dab069319912221e5838a132ab18a8bc84.json b/plugins/inputs/docker/testdata/stats_d4ccced494a1d5fe8ebdb0a86335a0dab069319912221e5838a132ab18a8bc84.json new file mode 100644 index 0000000000000..2aa28425d78fb --- /dev/null +++ b/plugins/inputs/docker/testdata/stats_d4ccced494a1d5fe8ebdb0a86335a0dab069319912221e5838a132ab18a8bc84.json @@ -0,0 +1,106 @@ +{ + "name": "/foo", + "blkio_stats": { + "io_service_bytes_recursive": [ + { + "major": 252, + "minor": 1, + "op": "Read", + "value": 753664 + }, + { + "major": 252, + "minor": 1, + "op": "Write" + }, + { + "major": 252, + "minor": 1, + "op": "Sync" + }, + { + "major": 252, + "minor": 1, + "op": "Async", + "value": 753664 + }, + { + "major": 252, + "minor": 1, + "op": "Total", + "value": 753664 + } + ], + "io_serviced_recursive": [ + { + "major": 252, + "minor": 1, + "op": "Read", + "value": 26 + }, + { + "major": 252, + "minor": 1, + "op": "Write" + }, + { + "major": 252, + "minor": 1, + "op": "Sync" + }, + { + "major": 252, + "minor": 1, + "op": "Async", + "value": 26 + }, + { + "major": 252, + "minor": 1, + "op": "Total", + "value": 26 + } + ] + }, + "cpu_stats": { + "cpu_usage": { + "percpu_usage": [ + 17871, + 4959158, + 1646137, + 1231652, + 11829401, + 244656, + 369972, + 0 + ], + "total_usage": 20298847, + "usage_in_usermode": 10000000 + }, + "system_cpu_usage": 24052607520000000, + "throttling_data": {} + }, + "memory_stats": { + "limit": 18935443456, + "stats": {} + }, + "precpu_stats": { + "cpu_usage": { + "percpu_usage": [ + 17871, + 4959158, + 1646137, + 1231652, + 11829401, + 244656, + 369972, + 0 + ], + "total_usage": 20298847, + "usage_in_usermode": 10000000 + }, + "system_cpu_usage": 24052599550000000, + "throttling_data": {} + }, + "read": "2016-02-24T11:42:27.472459608-05:00" +} diff --git a/plugins/inputs/docker/testdata/stats_e2173b9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296b7dfb.json b/plugins/inputs/docker/testdata/stats_e2173b9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296b7dfb.json new file mode 100644 index 0000000000000..fabf7f5b6bba8 --- /dev/null +++ b/plugins/inputs/docker/testdata/stats_e2173b9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296b7dfb.json @@ -0,0 +1,106 @@ +{ + "name": "etcd", + "blkio_stats": { + "io_service_bytes_recursive": [ + { + "major": 252, + "minor": 1, + "op": "Read", + "value": 753664 + }, + { + "major": 252, + "minor": 1, + "op": "Write" + }, + { + "major": 252, + "minor": 1, + "op": "Sync" + }, + { + "major": 252, + "minor": 1, + "op": "Async", + "value": 753664 + }, + { + "major": 252, + "minor": 1, + "op": "Total", + "value": 753664 + } + ], + "io_serviced_recursive": [ + { + "major": 252, + "minor": 1, + "op": "Read", + "value": 26 + }, + { + "major": 252, + "minor": 1, + "op": "Write" + }, + { + "major": 252, + "minor": 1, + "op": "Sync" + }, + { + "major": 252, + "minor": 1, + "op": "Async", + "value": 26 + }, + { + "major": 252, + "minor": 1, + "op": "Total", + "value": 26 + } + ] + }, + "cpu_stats": { + "cpu_usage": { + "percpu_usage": [ + 17871, + 4959158, + 1646137, + 1231652, + 11829401, + 244656, + 369972, + 0 + ], + "total_usage": 20298847, + "usage_in_usermode": 10000000 + }, + "system_cpu_usage": 24052607520000000, + "throttling_data": {} + }, + "memory_stats": { + "limit": 18935443456, + "stats": {} + }, + "precpu_stats": { + "cpu_usage": { + "percpu_usage": [ + 17871, + 4959158, + 1646137, + 1231652, + 11829401, + 244656, + 369972, + 0 + ], + "total_usage": 20298847, + "usage_in_usermode": 10000000 + }, + "system_cpu_usage": 24052599550000000, + "throttling_data": {} + }, + "read": "2016-02-24T11:42:27.472459608-05:00" +} diff --git a/plugins/inputs/docker/testdata/stats_e8a713dd90604f5a257b97c15945e047ab60ed5b2c4397c5a6b5bf40e1bd2791.json b/plugins/inputs/docker/testdata/stats_e8a713dd90604f5a257b97c15945e047ab60ed5b2c4397c5a6b5bf40e1bd2791.json new file mode 100644 index 0000000000000..99ae54a098a0a --- /dev/null +++ b/plugins/inputs/docker/testdata/stats_e8a713dd90604f5a257b97c15945e047ab60ed5b2c4397c5a6b5bf40e1bd2791.json @@ -0,0 +1,106 @@ +{ + "name": "/acme", + "blkio_stats": { + "io_service_bytes_recursive": [ + { + "major": 252, + "minor": 1, + "op": "Read", + "value": 753664 + }, + { + "major": 252, + "minor": 1, + "op": "Write" + }, + { + "major": 252, + "minor": 1, + "op": "Sync" + }, + { + "major": 252, + "minor": 1, + "op": "Async", + "value": 753664 + }, + { + "major": 252, + "minor": 1, + "op": "Total", + "value": 753664 + } + ], + "io_serviced_recursive": [ + { + "major": 252, + "minor": 1, + "op": "Read", + "value": 26 + }, + { + "major": 252, + "minor": 1, + "op": "Write" + }, + { + "major": 252, + "minor": 1, + "op": "Sync" + }, + { + "major": 252, + "minor": 1, + "op": "Async", + "value": 26 + }, + { + "major": 252, + "minor": 1, + "op": "Total", + "value": 26 + } + ] + }, + "cpu_stats": { + "cpu_usage": { + "percpu_usage": [ + 17871, + 4959158, + 1646137, + 1231652, + 11829401, + 244656, + 369972, + 0 + ], + "total_usage": 20298847, + "usage_in_usermode": 10000000 + }, + "system_cpu_usage": 24052607520000000, + "throttling_data": {} + }, + "memory_stats": { + "limit": 18935443456, + "stats": {} + }, + "precpu_stats": { + "cpu_usage": { + "percpu_usage": [ + 17871, + 4959158, + 1646137, + 1231652, + 11829401, + 244656, + 369972, + 0 + ], + "total_usage": 20298847, + "usage_in_usermode": 10000000 + }, + "system_cpu_usage": 24052599550000000, + "throttling_data": {} + }, + "read": "2016-02-24T11:42:27.472459608-05:00" +} diff --git a/plugins/inputs/docker/testdata/stats_windows_123456789.json b/plugins/inputs/docker/testdata/stats_windows_123456789.json new file mode 100644 index 0000000000000..97eacf6cba24a --- /dev/null +++ b/plugins/inputs/docker/testdata/stats_windows_123456789.json @@ -0,0 +1,35 @@ +{ + "read": "2017-01-11T08:32:46.2413794Z", + "preread": "0001-01-01T00:00:00Z", + "num_procs": 64, + "cpu_stats": { + "cpu_usage": { + "total_usage": 536718750, + "usage_in_kernelmode": 390468750, + "usage_in_usermode": 390468750 + }, + "throttling_data": { + "periods": 0, + "throttled_periods": 0, + "throttled_time": 0 + } + }, + "precpu_stats": { + "cpu_usage": { + "total_usage": 0, + "usage_in_kernelmode": 0, + "usage_in_usermode": 0 + }, + "throttling_data": { + "periods": 0, + "throttled_periods": 0, + "throttled_time": 0 + } + }, + "memory_stats": { + "commitbytes": 77160448, + "commitpeakbytes": 105000960, + "privateworkingset": 59961344 + }, + "name": "/gt_test_iis" +} \ No newline at end of file diff --git a/plugins/inputs/docker/testdata/tasks.json b/plugins/inputs/docker/testdata/tasks.json new file mode 100644 index 0000000000000..f67dff7991713 --- /dev/null +++ b/plugins/inputs/docker/testdata/tasks.json @@ -0,0 +1,59 @@ +[ + { + "ID": "kwh0lv7hwwbh", + "Version": {}, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "Labels": null, + "Spec": { + "ForceUpdate": 0 + }, + "ServiceID": "qolkls9g5iasdiuihcyz9rnx2", + "NodeID": "0cl4jturcyd1ks3fwpd010kor", + "Status": { + "Timestamp": "0001-01-01T00:00:00Z", + "State": "running", + "PortStatus": {} + }, + "DesiredState": "running", + "Volumes": null + }, + { + "ID": "u78m5ojbivc3", + "Version": {}, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "Labels": null, + "Spec": { + "ForceUpdate": 0 + }, + "ServiceID": "qolkls9g5iasdiuihcyz9rnx2", + "NodeID": "0cl4jturcyd1ks3fwpd010kor", + "Status": { + "Timestamp": "0001-01-01T00:00:00Z", + "State": "running", + "PortStatus": {} + }, + "DesiredState": "running", + "Volumes": null + }, + { + "ID": "1n1uilkhr98l", + "Version": {}, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "Labels": null, + "Spec": { + "ForceUpdate": 0 + }, + "ServiceID": "qolkls9g5iasdiuihcyz9rn3", + "NodeID": "0cl4jturcyd1ks3fwpd010kor", + "Status": { + "Timestamp": "0001-01-01T00:00:00Z", + "State": "running", + "PortStatus": {} + }, + "DesiredState": "running", + "Volumes": null + } +] \ No newline at end of file From 610a3bfda3abc04dacd05a6fcf82da2074a090ff Mon Sep 17 00:00:00 2001 From: Sven Rebhan <36194019+srebhan@users.noreply.github.com> Date: Mon, 4 May 2026 09:08:00 +0200 Subject: [PATCH 2/3] Update plugins/inputs/docker/docker_test.go Co-authored-by: skartikey --- plugins/inputs/docker/docker_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/inputs/docker/docker_test.go b/plugins/inputs/docker/docker_test.go index 25d86c3cd93c0..d1de558edb843 100644 --- a/plugins/inputs/docker/docker_test.go +++ b/plugins/inputs/docker/docker_test.go @@ -2047,7 +2047,7 @@ func readContainerData(path string) (*containerData, error) { if _, err := os.Stat(filepath.Join(path, "inspect.json")); err == nil { buf, err := os.ReadFile(filepath.Join(path, "inspect.json")) if err != nil { - return nil, fmt.Errorf("reading inspection datafailed: %w", err) + return nil, fmt.Errorf("reading inspection data failed: %w", err) } if err := json.Unmarshal(buf, &in.inspection); err != nil { return nil, fmt.Errorf("parsing inspection data failed: %w", err) From 89c2b401303848b59237754acbafab6be981a383 Mon Sep 17 00:00:00 2001 From: Sven Rebhan Date: Mon, 4 May 2026 10:27:47 +0200 Subject: [PATCH 3/3] Add back model stop calls --- plugins/inputs/docker/docker_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/inputs/docker/docker_test.go b/plugins/inputs/docker/docker_test.go index d1de558edb843..3944263c25338 100644 --- a/plugins/inputs/docker/docker_test.go +++ b/plugins/inputs/docker/docker_test.go @@ -1830,6 +1830,7 @@ func TestStartupErrorBehaviorError(t *testing.T) { // Starting the plugin will fail with an error because Ping fails var acc testutil.Accumulator require.ErrorContains(t, model.Start(&acc), "failed to ping Docker daemon") + model.Stop() } func TestStartupErrorBehaviorIgnore(t *testing.T) { @@ -1855,6 +1856,7 @@ func TestStartupErrorBehaviorIgnore(t *testing.T) { // Starting the plugin will fail and model should convert to fatal error var acc testutil.Accumulator require.ErrorContains(t, model.Start(&acc), "failed to ping Docker daemon") + model.Stop() } func TestStartSuccess(t *testing.T) {