Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add filters for container state to docker input #3950

Merged
merged 4 commits into from
Mar 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions plugins/inputs/docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
47 changes: 44 additions & 3 deletions plugins/inputs/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"`

Expand All @@ -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"`
Expand All @@ -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
Expand All @@ -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 = `
Expand All @@ -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"

Expand Down Expand Up @@ -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
}

Expand All @@ -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)
Expand Down Expand Up @@ -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{
Expand Down
83 changes: 83 additions & 0 deletions plugins/inputs/docker/docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package docker
import (
"context"
"crypto/tls"
"sort"
"testing"

"github.com/influxdata/telegraf/testutil"
Expand Down Expand Up @@ -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)
})
}
}