Docker: optionally add labels as tags (#2425)

This commit is contained in:
Shakeel Sorathia 2017-04-03 13:43:15 -07:00 committed by Daniel Nelson
parent 0def641ce8
commit 95a9d904e4
5 changed files with 135 additions and 16 deletions

View File

@ -63,6 +63,7 @@ be deprecated eventually.
- [#2332](https://github.com/influxdata/telegraf/pull/2332): Add Elasticsearch 5.x output - [#2332](https://github.com/influxdata/telegraf/pull/2332): Add Elasticsearch 5.x output
- [#2587](https://github.com/influxdata/telegraf/pull/2587): Add json timestamp units configurability - [#2587](https://github.com/influxdata/telegraf/pull/2587): Add json timestamp units configurability
- [#2597](https://github.com/influxdata/telegraf/issues/2597): Add support for Linux sysctl-fs metrics. - [#2597](https://github.com/influxdata/telegraf/issues/2597): Add support for Linux sysctl-fs metrics.
- [#2425](https://github.com/influxdata/telegraf/pull/2425): Support to include/exclude docker container labels as tags
### Bugfixes ### Bugfixes

View File

@ -30,6 +30,12 @@ for the stat structure can be found
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
## docker labels to include and exclude as tags. Globs accepted.
## Note that an empty array for both will include all labels as tags
docker_label_include = []
docker_label_exclude = []
``` ```
### Measurements & Fields: ### Measurements & Fields:
@ -130,30 +136,32 @@ based on the availability of per-cpu stats on your system.
### Tags: ### Tags:
#### Docker Engine tags
- docker (memory_total) - docker (memory_total)
- unit=bytes - unit=bytes
- engine_host
- docker (pool_blocksize) - docker (pool_blocksize)
- unit=bytes - unit=bytes
- engine_host
- docker_data - docker_data
- unit=bytes - unit=bytes
- engine_host
- docker_metadata - docker_metadata
- unit=bytes - unit=bytes
- engine_host
#### Docker Container tags
- Tags on all containers:
- engine_host
- container_image
- container_name
- container_version
- docker_container_mem specific: - docker_container_mem specific:
- container_image
- container_name
- docker_container_cpu specific: - docker_container_cpu specific:
- container_image
- container_name
- cpu - cpu
- docker_container_net specific: - docker_container_net specific:
- container_image
- container_name
- network - network
- docker_container_blkio specific: - docker_container_blkio specific:
- container_image
- container_name
- device - device
### Example Output: ### Example Output:

View File

@ -14,12 +14,17 @@ import (
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/influxdata/telegraf" "github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/filter"
"github.com/influxdata/telegraf/internal" "github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/plugins/inputs" "github.com/influxdata/telegraf/plugins/inputs"
) )
type DockerLabelFilter struct {
labelInclude filter.Filter
labelExclude filter.Filter
}
// Docker object // Docker object
type Docker struct { type Docker struct {
Endpoint string Endpoint string
@ -27,11 +32,16 @@ type Docker struct {
Timeout internal.Duration Timeout internal.Duration
PerDevice bool `toml:"perdevice"` PerDevice bool `toml:"perdevice"`
Total bool `toml:"total"` Total bool `toml:"total"`
LabelInclude []string `toml:"docker_label_include"`
LabelExclude []string `toml:"docker_label_exclude"`
LabelFilter DockerLabelFilter
client *client.Client client *client.Client
engine_host string engine_host string
testing bool testing bool
labelFiltersCreated bool
} }
// infoWrapper wraps client.Client.List for testing. // infoWrapper wraps client.Client.List for testing.
@ -99,6 +109,10 @@ var sampleConfig = `
## 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
## docker labels to include and exclude as tags. Globs accepted.
## Note that an empty array for both will include all labels as tags
docker_label_include = []
docker_label_exclude = []
` `
// Description returns input description // Description returns input description
@ -133,6 +147,14 @@ func (d *Docker) Gather(acc telegraf.Accumulator) error {
} }
d.client = c d.client = c
} }
// Create label filters if not already created
if !d.labelFiltersCreated {
err := d.createLabelFilters()
if err != nil {
return err
}
d.labelFiltersCreated = true
}
// Get daemon info // Get daemon info
err := d.gatherInfo(acc) err := d.gatherInfo(acc)
@ -293,8 +315,12 @@ func (d *Docker) gatherContainer(
// Add labels to tags // Add labels to tags
for k, label := range container.Labels { for k, label := range container.Labels {
if len(d.LabelInclude) == 0 || d.LabelFilter.labelInclude.Match(k) {
if len(d.LabelExclude) == 0 || !d.LabelFilter.labelExclude.Match(k) {
tags[k] = label tags[k] = label
} }
}
}
gatherContainerStats(v, acc, tags, container.ID, d.PerDevice, d.Total) gatherContainerStats(v, acc, tags, container.ID, d.PerDevice, d.Total)
@ -599,11 +625,32 @@ func parseSize(sizeStr string) (int64, error) {
return int64(size), nil return int64(size), nil
} }
func (d *Docker) createLabelFilters() error {
if len(d.LabelInclude) != 0 && d.LabelFilter.labelInclude == nil {
var err error
d.LabelFilter.labelInclude, err = filter.Compile(d.LabelInclude)
if err != nil {
return err
}
}
if len(d.LabelExclude) != 0 && d.LabelFilter.labelExclude == nil {
var err error
d.LabelFilter.labelExclude, err = filter.Compile(d.LabelExclude)
if err != nil {
return err
}
}
return nil
}
func init() { func init() {
inputs.Add("docker", func() telegraf.Input { inputs.Add("docker", func() telegraf.Input {
return &Docker{ return &Docker{
PerDevice: true, PerDevice: true,
Timeout: internal.Duration{Duration: time.Second * 5}, Timeout: internal.Duration{Duration: time.Second * 5},
labelFiltersCreated: false,
} }
}) })
} }

View File

@ -244,6 +244,57 @@ func testStats() *types.StatsJSON {
return stats return stats
} }
var gatherLabelsTests = []struct {
include []string
exclude []string
expected []string
notexpected []string
}{
{[]string{}, []string{}, []string{"label1", "label2"}, []string{}},
{[]string{"*"}, []string{}, []string{"label1", "label2"}, []string{}},
{[]string{"lab*"}, []string{}, []string{"label1", "label2"}, []string{}},
{[]string{"label1"}, []string{}, []string{"label1"}, []string{"label2"}},
{[]string{"label1*"}, []string{}, []string{"label1"}, []string{"label2"}},
{[]string{}, []string{"*"}, []string{}, []string{"label1", "label2"}},
{[]string{}, []string{"lab*"}, []string{}, []string{"label1", "label2"}},
{[]string{}, []string{"label1"}, []string{"label2"}, []string{"label1"}},
{[]string{"*"}, []string{"*"}, []string{}, []string{"label1", "label2"}},
}
func TestDockerGatherLabels(t *testing.T) {
for _, tt := range gatherLabelsTests {
var acc testutil.Accumulator
d := Docker{
client: nil,
testing: true,
}
for _, label := range tt.include {
d.LabelInclude = append(d.LabelInclude, label)
}
for _, label := range tt.exclude {
d.LabelExclude = append(d.LabelExclude, label)
}
err := d.Gather(&acc)
require.NoError(t, err)
for _, label := range tt.expected {
if !acc.HasTag("docker_container_cpu", label) {
t.Errorf("Didn't get expected label of %s. Test was: Include: %s Exclude %s",
label, tt.include, tt.exclude)
}
}
for _, label := range tt.notexpected {
if acc.HasTag("docker_container_cpu", label) {
t.Errorf("Got unexpected label of %s. Test was: Include: %s Exclude %s",
label, 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{
@ -294,6 +345,8 @@ func TestDockerGatherInfo(t *testing.T) {
"cpu": "cpu3", "cpu": "cpu3",
"container_version": "v2.2.2", "container_version": "v2.2.2",
"engine_host": "absol", "engine_host": "absol",
"label1": "test_value_1",
"label2": "test_value_2",
}, },
) )
acc.AssertContainsTaggedFields(t, acc.AssertContainsTaggedFields(t,
@ -340,6 +393,8 @@ func TestDockerGatherInfo(t *testing.T) {
"container_name": "etcd2", "container_name": "etcd2",
"container_image": "quay.io:4443/coreos/etcd", "container_image": "quay.io:4443/coreos/etcd",
"container_version": "v2.2.2", "container_version": "v2.2.2",
"label1": "test_value_1",
"label2": "test_value_2",
}, },
) )

View File

@ -92,6 +92,10 @@ func (d FakeDockerClient) ContainerList(octx context.Context, options types.Cont
IP: "0.0.0.0", IP: "0.0.0.0",
}, },
}, },
Labels: map[string]string{
"label1": "test_value_1",
"label2": "test_value_2",
},
SizeRw: 0, SizeRw: 0,
SizeRootFs: 0, SizeRootFs: 0,
} }
@ -125,6 +129,10 @@ func (d FakeDockerClient) ContainerList(octx context.Context, options types.Cont
IP: "0.0.0.0", IP: "0.0.0.0",
}, },
}, },
Labels: map[string]string{
"label1": "test_value_1",
"label2": "test_value_2",
},
SizeRw: 0, SizeRw: 0,
SizeRootFs: 0, SizeRootFs: 0,
} }