diff --git a/plugins/inputs/docker/README.md b/plugins/inputs/docker/README.md index 7e99075886c39..b833ff26482f1 100644 --- a/plugins/inputs/docker/README.md +++ b/plugins/inputs/docker/README.md @@ -31,6 +31,11 @@ to gather stats from the [Engine API](https://docs.docker.com/engine/api/v1.20/) container_name_include = [] container_name_exclude = [] + ## Container states to include and exclude. Globs accepted. + ## When empty only containers in the "running" state will be captured. + # container_state_include = [] + # container_state_exclude = [] + ## Timeout for docker list, info, and stats commands timeout = "5s" diff --git a/plugins/inputs/docker/docker.go b/plugins/inputs/docker/docker.go index 7178eb497e9b8..de77238936910 100644 --- a/plugins/inputs/docker/docker.go +++ b/plugins/inputs/docker/docker.go @@ -15,6 +15,7 @@ import ( "time" "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/swarm" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/filter" @@ -25,7 +26,7 @@ import ( // Docker object type Docker struct { Endpoint string - ContainerNames []string + ContainerNames []string // deprecated in 1.4; use container_name_include GatherServices bool `toml:"gather_services"` @@ -39,6 +40,9 @@ type Docker struct { ContainerInclude []string `toml:"container_name_include"` ContainerExclude []string `toml:"container_name_exclude"` + ContainerStateInclude []string `toml:"container_state_include"` + ContainerStateExclude []string `toml:"container_state_exclude"` + SSLCA string `toml:"ssl_ca"` SSLCert string `toml:"ssl_cert"` SSLKey string `toml:"ssl_key"` @@ -53,6 +57,7 @@ type Docker struct { filtersCreated bool labelFilter filter.Filter containerFilter filter.Filter + stateFilter filter.Filter } // KB, MB, GB, TB, PB...human friendly @@ -67,7 +72,8 @@ const ( ) var ( - sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[bB]?$`) + sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[bB]?$`) + containerStates = []string{"created", "restarting", "running", "removing", "paused", "exited", "dead"} ) var sampleConfig = ` @@ -87,6 +93,11 @@ var sampleConfig = ` container_name_include = [] container_name_exclude = [] + ## Container states to include and exclude. Globs accepted. + ## When empty only containers in the "running" state will be captured. + # container_state_include = [] + # container_state_exclude = [] + ## Timeout for docker list, info, and stats commands timeout = "5s" @@ -148,6 +159,10 @@ func (d *Docker) Gather(acc telegraf.Accumulator) error { if err != nil { return err } + err = d.createContainerStateFilters() + if err != nil { + return err + } d.filtersCreated = true } @@ -164,8 +179,22 @@ func (d *Docker) Gather(acc telegraf.Accumulator) error { } } + filterArgs := filters.NewArgs() + for _, state := range containerStates { + if d.stateFilter.Match(state) { + filterArgs.Add("status", state) + } + } + + // All container states were excluded + if filterArgs.Len() == 0 { + return nil + } + // List containers - opts := types.ContainerListOptions{} + opts := types.ContainerListOptions{ + Filters: filterArgs, + } ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration) defer cancel() containers, err := d.client.ContainerList(ctx, opts) @@ -768,6 +797,18 @@ func (d *Docker) createLabelFilters() error { return nil } +func (d *Docker) createContainerStateFilters() error { + if len(d.ContainerStateInclude) == 0 && len(d.ContainerStateExclude) == 0 { + d.ContainerStateInclude = []string{"running"} + } + filter, err := filter.NewIncludeExcludeFilter(d.ContainerStateInclude, d.ContainerStateExclude) + if err != nil { + return err + } + d.stateFilter = filter + return nil +} + func init() { inputs.Add("docker", func() telegraf.Input { return &Docker{ diff --git a/plugins/inputs/docker/docker_test.go b/plugins/inputs/docker/docker_test.go index 088c94597ab05..0bc5d33d46fe1 100644 --- a/plugins/inputs/docker/docker_test.go +++ b/plugins/inputs/docker/docker_test.go @@ -3,6 +3,7 @@ package docker import ( "context" "crypto/tls" + "sort" "testing" "github.com/influxdata/telegraf/testutil" @@ -711,3 +712,85 @@ func TestDockerGatherSwarmInfo(t *testing.T) { }, ) } + +func TestContainerStateFilter(t *testing.T) { + var tests = []struct { + name string + include []string + exclude []string + expected map[string][]string + }{ + { + name: "default", + expected: map[string][]string{ + "status": []string{"running"}, + }, + }, + { + name: "include running", + include: []string{"running"}, + expected: map[string][]string{ + "status": []string{"running"}, + }, + }, + { + name: "include glob", + include: []string{"r*"}, + expected: map[string][]string{ + "status": []string{"restarting", "running", "removing"}, + }, + }, + { + name: "include all", + include: []string{"*"}, + expected: map[string][]string{ + "status": []string{"created", "restarting", "running", "removing", "paused", "exited", "dead"}, + }, + }, + { + name: "exclude all", + exclude: []string{"*"}, + expected: map[string][]string{ + "status": []string{}, + }, + }, + { + name: "exclude all", + include: []string{"*"}, + exclude: []string{"exited"}, + expected: map[string][]string{ + "status": []string{"created", "restarting", "running", "removing", "paused", "dead"}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var acc testutil.Accumulator + + newClientFunc := func(host string, tlsConfig *tls.Config) (Client, error) { + client := baseClient + client.ContainerListF = func(ctx context.Context, options types.ContainerListOptions) ([]types.Container, error) { + for k, v := range tt.expected { + actual := options.Filters.Get(k) + sort.Strings(actual) + sort.Strings(v) + require.Equal(t, v, actual) + } + + return nil, nil + } + return &client, nil + } + + d := Docker{ + newClient: newClientFunc, + ContainerStateInclude: tt.include, + ContainerStateExclude: tt.exclude, + } + + err := d.Gather(&acc) + require.NoError(t, err) + }) + } +}