Add filters for container state to docker input (#3950)

This commit is contained in:
Daniel Nelson 2018-03-30 13:17:48 -07:00 committed by GitHub
parent 0d5759daed
commit 005d7823a5
3 changed files with 132 additions and 3 deletions

View File

@ -31,6 +31,11 @@ to gather stats from the [Engine API](https://docs.docker.com/engine/api/v1.20/)
container_name_include = [] container_name_include = []
container_name_exclude = [] 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 for docker list, info, and stats commands
timeout = "5s" timeout = "5s"

View File

@ -15,6 +15,7 @@ import (
"time" "time"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/swarm"
"github.com/influxdata/telegraf" "github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/filter" "github.com/influxdata/telegraf/filter"
@ -25,7 +26,7 @@ import (
// Docker object // Docker object
type Docker struct { type Docker struct {
Endpoint string Endpoint string
ContainerNames []string ContainerNames []string // deprecated in 1.4; use container_name_include
GatherServices bool `toml:"gather_services"` GatherServices bool `toml:"gather_services"`
@ -39,6 +40,9 @@ type Docker struct {
ContainerInclude []string `toml:"container_name_include"` ContainerInclude []string `toml:"container_name_include"`
ContainerExclude []string `toml:"container_name_exclude"` ContainerExclude []string `toml:"container_name_exclude"`
ContainerStateInclude []string `toml:"container_state_include"`
ContainerStateExclude []string `toml:"container_state_exclude"`
SSLCA string `toml:"ssl_ca"` SSLCA string `toml:"ssl_ca"`
SSLCert string `toml:"ssl_cert"` SSLCert string `toml:"ssl_cert"`
SSLKey string `toml:"ssl_key"` SSLKey string `toml:"ssl_key"`
@ -53,6 +57,7 @@ type Docker struct {
filtersCreated bool filtersCreated bool
labelFilter filter.Filter labelFilter filter.Filter
containerFilter filter.Filter containerFilter filter.Filter
stateFilter filter.Filter
} }
// KB, MB, GB, TB, PB...human friendly // KB, MB, GB, TB, PB...human friendly
@ -68,6 +73,7 @@ const (
var ( 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 = ` var sampleConfig = `
@ -87,6 +93,11 @@ var sampleConfig = `
container_name_include = [] container_name_include = []
container_name_exclude = [] 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 for docker list, info, and stats commands
timeout = "5s" timeout = "5s"
@ -148,6 +159,10 @@ func (d *Docker) Gather(acc telegraf.Accumulator) error {
if err != nil { if err != nil {
return err return err
} }
err = d.createContainerStateFilters()
if err != nil {
return err
}
d.filtersCreated = true 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 // List containers
opts := types.ContainerListOptions{} opts := types.ContainerListOptions{
Filters: filterArgs,
}
ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration) ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration)
defer cancel() defer cancel()
containers, err := d.client.ContainerList(ctx, opts) containers, err := d.client.ContainerList(ctx, opts)
@ -768,6 +797,18 @@ func (d *Docker) createLabelFilters() error {
return nil 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() { func init() {
inputs.Add("docker", func() telegraf.Input { inputs.Add("docker", func() telegraf.Input {
return &Docker{ return &Docker{

View File

@ -3,6 +3,7 @@ package docker
import ( import (
"context" "context"
"crypto/tls" "crypto/tls"
"sort"
"testing" "testing"
"github.com/influxdata/telegraf/testutil" "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)
})
}
}