Add wildcard support for container inclusion/exclusion (#2793)

This commit is contained in:
Matteo Cerutti 2017-06-08 21:17:31 +01:00 committed by Daniel Nelson
parent d5c7fa206c
commit d7d64a76fe
3 changed files with 138 additions and 14 deletions

View File

@ -20,14 +20,22 @@ for the stat structure can be found
## To use TCP, set endpoint = "tcp://[ip]:[port]" ## To use TCP, set endpoint = "tcp://[ip]:[port]"
## To use environment variables (ie, docker-machine), set endpoint = "ENV" ## To use environment variables (ie, docker-machine), set endpoint = "ENV"
endpoint = "unix:///var/run/docker.sock" endpoint = "unix:///var/run/docker.sock"
## Only collect metrics for these containers, collect all if empty
## Only collect metrics for these containers. Values will be appended to container_name_include.
## Deprecated (1.4.0), use container_name_include
container_names = [] container_names = []
## Containers to include and exclude. Collect all if empty. Globs accepted.
container_name_include = []
container_name_exclude = []
## Timeout for docker list, info, and stats commands ## Timeout for docker list, info, and stats commands
timeout = "5s" timeout = "5s"
## Whether to report for each container per-device blkio (8:0, 8:1...) and ## Whether to report for each container per-device blkio (8:0, 8:1...) and
## network (eth0, eth1, ...) stats or not ## network (eth0, eth1, ...) stats or not
perdevice = true perdevice = true
## Whether to report for each container total blkio and network stats or not ## Whether to report for each container total blkio and network stats or not
total = false total = false

View File

@ -24,24 +24,33 @@ type DockerLabelFilter struct {
labelExclude filter.Filter labelExclude filter.Filter
} }
type DockerContainerFilter struct {
containerInclude filter.Filter
containerExclude filter.Filter
}
// Docker object // Docker object
type Docker struct { type Docker struct {
Endpoint string Endpoint string
ContainerNames []string ContainerNames []string
Timeout internal.Duration Timeout internal.Duration
PerDevice bool `toml:"perdevice"` PerDevice bool `toml:"perdevice"`
Total bool `toml:"total"` Total bool `toml:"total"`
TagEnvironment []string `toml:"tag_env"` TagEnvironment []string `toml:"tag_env"`
LabelInclude []string `toml:"docker_label_include"` LabelInclude []string `toml:"docker_label_include"`
LabelExclude []string `toml:"docker_label_exclude"` LabelExclude []string `toml:"docker_label_exclude"`
LabelFilter DockerLabelFilter LabelFilter DockerLabelFilter
ContainerInclude []string `toml:"container_name_include"`
ContainerExclude []string `toml:"container_name_exclude"`
ContainerFilter DockerContainerFilter
client *client.Client client *client.Client
engine_host string engine_host string
testing bool testing bool
labelFiltersCreated bool filtersCreated bool
} }
// infoWrapper wraps client.Client.List for testing. // infoWrapper wraps client.Client.List for testing.
@ -110,8 +119,15 @@ var sampleConfig = `
## To use TCP, set endpoint = "tcp://[ip]:[port]" ## To use TCP, set endpoint = "tcp://[ip]:[port]"
## To use environment variables (ie, docker-machine), set endpoint = "ENV" ## To use environment variables (ie, docker-machine), set endpoint = "ENV"
endpoint = "unix:///var/run/docker.sock" endpoint = "unix:///var/run/docker.sock"
## Only collect metrics for these containers, collect all if empty ## Only collect metrics for these containers, collect all if empty
container_names = [] container_names = []
## Containers to include and exclude. Globs accepted.
## Note that an empty array for both will include all containers
container_name_include = []
container_name_exclude = []
## Timeout for docker list, info, and stats commands ## Timeout for docker list, info, and stats commands
timeout = "5s" timeout = "5s"
@ -161,13 +177,18 @@ func (d *Docker) Gather(acc telegraf.Accumulator) error {
} }
d.client = c d.client = c
} }
// Create label filters if not already created // Create label filters if not already created
if !d.labelFiltersCreated { if !d.filtersCreated {
err := d.createLabelFilters() err := d.createLabelFilters()
if err != nil { if err != nil {
return err return err
} }
d.labelFiltersCreated = true err = d.createContainerFilters()
if err != nil {
return err
}
d.filtersCreated = true
} }
// Get daemon info // Get daemon info
@ -306,11 +327,14 @@ func (d *Docker) gatherContainer(
"container_image": imageName, "container_image": imageName,
"container_version": imageVersion, "container_version": imageVersion,
} }
if len(d.ContainerNames) > 0 {
if !sliceContains(cname, d.ContainerNames) { if len(d.ContainerInclude) > 0 || len(d.ContainerExclude) > 0 {
if len(d.ContainerInclude) == 0 || !d.ContainerFilter.containerInclude.Match(cname) {
if len(d.ContainerExclude) == 0 || d.ContainerFilter.containerExclude.Match(cname) {
return nil return nil
} }
} }
}
ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration) ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration)
defer cancel() defer cancel()
@ -656,8 +680,32 @@ func parseSize(sizeStr string) (int64, error) {
return int64(size), nil return int64(size), nil
} }
func (d *Docker) createContainerFilters() error {
if len(d.ContainerNames) > 0 {
d.ContainerInclude = append(d.ContainerInclude, d.ContainerNames...)
}
if len(d.ContainerInclude) != 0 {
var err error
d.ContainerFilter.containerInclude, err = filter.Compile(d.ContainerInclude)
if err != nil {
return err
}
}
if len(d.ContainerExclude) != 0 {
var err error
d.ContainerFilter.containerExclude, err = filter.Compile(d.ContainerExclude)
if err != nil {
return err
}
}
return nil
}
func (d *Docker) createLabelFilters() error { func (d *Docker) createLabelFilters() error {
if len(d.LabelInclude) != 0 && d.LabelFilter.labelInclude == nil { if len(d.LabelInclude) != 0 {
var err error var err error
d.LabelFilter.labelInclude, err = filter.Compile(d.LabelInclude) d.LabelFilter.labelInclude, err = filter.Compile(d.LabelInclude)
if err != nil { if err != nil {
@ -665,7 +713,7 @@ func (d *Docker) createLabelFilters() error {
} }
} }
if len(d.LabelExclude) != 0 && d.LabelFilter.labelExclude == nil { if len(d.LabelExclude) != 0 {
var err error var err error
d.LabelFilter.labelExclude, err = filter.Compile(d.LabelExclude) d.LabelFilter.labelExclude, err = filter.Compile(d.LabelExclude)
if err != nil { if err != nil {
@ -681,7 +729,7 @@ func init() {
return &Docker{ return &Docker{
PerDevice: true, PerDevice: true,
Timeout: internal.Duration{Duration: time.Second * 5}, Timeout: internal.Duration{Duration: time.Second * 5},
labelFiltersCreated: false, filtersCreated: false,
} }
}) })
} }

View File

@ -18,6 +18,7 @@ func TestDockerGatherContainerStats(t *testing.T) {
"container_name": "redis", "container_name": "redis",
"container_image": "redis/image", "container_image": "redis/image",
} }
gatherContainerStats(stats, &acc, tags, "123456789", true, true) gatherContainerStats(stats, &acc, tags, "123456789", true, true)
// test docker_container_net measurement // test docker_container_net measurement
@ -295,6 +296,73 @@ func TestDockerGatherLabels(t *testing.T) {
} }
} }
var gatherContainerNames = []struct {
include []string
exclude []string
expected []string
notexpected []string
}{
{[]string{}, []string{}, []string{"etcd", "etcd2"}, []string{}},
{[]string{"*"}, []string{}, []string{"etcd", "etcd2"}, []string{}},
{[]string{"etc*"}, []string{}, []string{"etcd", "etcd2"}, []string{}},
{[]string{"etcd"}, []string{}, []string{"etcd"}, []string{"etcd2"}},
{[]string{"etcd2*"}, []string{}, []string{"etcd2"}, []string{"etcd"}},
{[]string{}, []string{"etc*"}, []string{}, []string{"etcd", "etcd2"}},
{[]string{}, []string{"etcd"}, []string{"etcd2"}, []string{"etcd"}},
{[]string{"*"}, []string{"*"}, []string{"etcd", "etcd2"}, []string{}},
{[]string{}, []string{"*"}, []string{""}, []string{"etcd", "etcd2"}},
}
func TestContainerNames(t *testing.T) {
for _, tt := range gatherContainerNames {
var acc testutil.Accumulator
d := Docker{
client: nil,
testing: true,
ContainerInclude: tt.include,
ContainerExclude: tt.exclude,
}
err := d.Gather(&acc)
require.NoError(t, err)
for _, metric := range acc.Metrics {
if metric.Measurement == "docker_container_cpu" {
if val, ok := metric.Tags["container_name"]; ok {
var found bool = false
for _, cname := range tt.expected {
if val == cname {
found = true
break
}
}
if !found {
t.Errorf("Got unexpected container of %s. Test was -> Include: %s, Exclude: %s", val, tt.include, tt.exclude)
}
}
}
}
for _, metric := range acc.Metrics {
if metric.Measurement == "docker_container_cpu" {
if val, ok := metric.Tags["container_name"]; ok {
var found bool = false
for _, cname := range tt.notexpected {
if val == cname {
found = true
break
}
}
if found {
t.Errorf("Got unexpected container of %s. Test was -> Include: %s, Exclude: %s", val, tt.include, tt.exclude)
}
}
}
}
}
}
func TestDockerGatherInfo(t *testing.T) { func TestDockerGatherInfo(t *testing.T) {
var acc testutil.Accumulator var acc testutil.Accumulator
d := Docker{ d := Docker{