Add container uptime_ns in docker input plugin (#5996)

This commit is contained in:
George 2019-06-19 23:37:10 +01:00 committed by Daniel Nelson
parent 2aeaed2f27
commit c52e7d88d7
4 changed files with 175 additions and 34 deletions

View File

@ -278,6 +278,7 @@ status if configured.
- exitcode (integer) - exitcode (integer)
- started_at (integer) - started_at (integer)
- finished_at (integer) - finished_at (integer)
- uptime_ns (integer)
- docker_swarm - docker_swarm
- tags: - tags:

View File

@ -73,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"} containerStates = []string{"created", "restarting", "running", "removing", "paused", "exited", "dead"}
now = time.Now
) )
var sampleConfig = ` var sampleConfig = `
@ -462,14 +463,21 @@ func (d *Docker) gatherContainer(
"pid": info.State.Pid, "pid": info.State.Pid,
"exitcode": info.State.ExitCode, "exitcode": info.State.ExitCode,
} }
container_time, err := time.Parse(time.RFC3339, info.State.StartedAt)
if err == nil && !container_time.IsZero() { finished, err := time.Parse(time.RFC3339, info.State.FinishedAt)
statefields["started_at"] = container_time.UnixNano() if err == nil && !finished.IsZero() {
statefields["finished_at"] = finished.UnixNano()
} else {
// set finished to now for use in uptime
finished = now()
} }
container_time, err = time.Parse(time.RFC3339, info.State.FinishedAt)
if err == nil && !container_time.IsZero() { started, err := time.Parse(time.RFC3339, info.State.StartedAt)
statefields["finished_at"] = container_time.UnixNano() if err == nil && !started.IsZero() {
statefields["started_at"] = started.UnixNano()
statefields["uptime_ns"] = finished.Sub(started).Nanoseconds()
} }
acc.AddFields("docker_container_status", statefields, tags, time.Now()) acc.AddFields("docker_container_status", statefields, tags, time.Now())
if info.State.Health != nil { if info.State.Health != nil {

View File

@ -7,6 +7,7 @@ import (
"sort" "sort"
"strings" "strings"
"testing" "testing"
"time"
"github.com/influxdata/telegraf/testutil" "github.com/influxdata/telegraf/testutil"
@ -83,7 +84,7 @@ var baseClient = MockClient{
return containerStats(s), nil return containerStats(s), nil
}, },
ContainerInspectF: func(context.Context, string) (types.ContainerJSON, error) { ContainerInspectF: func(context.Context, string) (types.ContainerJSON, error) {
return containerInspect, nil return containerInspect(), nil
}, },
ServiceListF: func(context.Context, types.ServiceListOptions) ([]swarm.Service, error) { ServiceListF: func(context.Context, types.ServiceListOptions) ([]swarm.Service, error) {
return ServiceList, nil return ServiceList, nil
@ -264,7 +265,7 @@ func TestDocker_WindowsMemoryContainerStats(t *testing.T) {
return containerStatsWindows(), nil return containerStatsWindows(), nil
}, },
ContainerInspectF: func(ctx context.Context, containerID string) (types.ContainerJSON, error) { ContainerInspectF: func(ctx context.Context, containerID string) (types.ContainerJSON, error) {
return containerInspect, nil return containerInspect(), nil
}, },
ServiceListF: func(context.Context, types.ServiceListOptions) ([]swarm.Service, error) { ServiceListF: func(context.Context, types.ServiceListOptions) ([]swarm.Service, error) {
return ServiceList, nil return ServiceList, nil
@ -538,6 +539,135 @@ func TestContainerNames(t *testing.T) {
} }
} }
func TestContainerStatus(t *testing.T) {
type expectation struct {
// tags
Status string
// fields
OOMKilled bool
Pid int
ExitCode int
StartedAt time.Time
FinishedAt time.Time
UptimeNs int64
}
var tests = []struct {
name string
now func() time.Time
inspect types.ContainerJSON
expect expectation
}{
{
name: "finished_at is zero value",
now: func() time.Time {
return time.Date(2018, 6, 14, 5, 51, 53, 266176036, time.UTC)
},
inspect: containerInspect(),
expect: expectation{
Status: "running",
OOMKilled: false,
Pid: 1234,
ExitCode: 0,
StartedAt: time.Date(2018, 6, 14, 5, 48, 53, 266176036, time.UTC),
UptimeNs: int64(3 * time.Minute),
},
},
{
name: "finished_at is non-zero value",
inspect: func() types.ContainerJSON {
i := containerInspect()
i.ContainerJSONBase.State.FinishedAt = "2018-06-14T05:53:53.266176036Z"
return i
}(),
expect: expectation{
Status: "running",
OOMKilled: false,
Pid: 1234,
ExitCode: 0,
StartedAt: time.Date(2018, 6, 14, 5, 48, 53, 266176036, time.UTC),
FinishedAt: time.Date(2018, 6, 14, 5, 53, 53, 266176036, time.UTC),
UptimeNs: int64(5 * time.Minute),
},
},
{
name: "started_at is zero value",
inspect: func() types.ContainerJSON {
i := containerInspect()
i.ContainerJSONBase.State.StartedAt = ""
i.ContainerJSONBase.State.FinishedAt = "2018-06-14T05:53:53.266176036Z"
return i
}(),
expect: expectation{
Status: "running",
OOMKilled: false,
Pid: 1234,
ExitCode: 0,
FinishedAt: time.Date(2018, 6, 14, 5, 53, 53, 266176036, time.UTC),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var (
acc testutil.Accumulator
newClientFunc = func(string, *tls.Config) (Client, error) {
client := baseClient
client.ContainerListF = func(context.Context, types.ContainerListOptions) ([]types.Container, error) {
return containerList[:1], nil
}
client.ContainerInspectF = func(c context.Context, s string) (types.ContainerJSON, error) {
return tt.inspect, nil
}
return &client, nil
}
d = Docker{newClient: newClientFunc}
)
// mock time
if tt.now != nil {
now = tt.now
}
defer func() {
now = time.Now
}()
err := acc.GatherError(d.Gather)
require.NoError(t, err)
fields := map[string]interface{}{
"oomkilled": tt.expect.OOMKilled,
"pid": tt.expect.Pid,
"exitcode": tt.expect.ExitCode,
}
if started := tt.expect.StartedAt; !started.IsZero() {
fields["started_at"] = started.UnixNano()
fields["uptime_ns"] = tt.expect.UptimeNs
}
if finished := tt.expect.FinishedAt; !finished.IsZero() {
fields["finished_at"] = finished.UnixNano()
}
acc.AssertContainsTaggedFields(t,
"docker_container_status",
fields,
map[string]string{
"container_name": "etcd",
"container_image": "quay.io/coreos/etcd",
"container_version": "v2.2.2",
"engine_host": "absol",
"label1": "test_value_1",
"label2": "test_value_2",
"server_version": "17.09.0-ce",
"container_status": tt.expect.Status,
})
})
}
}
func TestDockerGatherInfo(t *testing.T) { func TestDockerGatherInfo(t *testing.T) {
var acc testutil.Accumulator var acc testutil.Accumulator
d := Docker{ d := Docker{

View File

@ -492,32 +492,34 @@ func containerStatsWindows() types.ContainerStats {
return stat return stat
} }
var containerInspect = types.ContainerJSON{ func containerInspect() types.ContainerJSON {
Config: &container.Config{ return types.ContainerJSON{
Env: []string{ Config: &container.Config{
"ENVVAR1=loremipsum", Env: []string{
"ENVVAR1FOO=loremipsum", "ENVVAR1=loremipsum",
"ENVVAR2=dolorsitamet", "ENVVAR1FOO=loremipsum",
"ENVVAR3==ubuntu:10.04", "ENVVAR2=dolorsitamet",
"ENVVAR4", "ENVVAR3==ubuntu:10.04",
"ENVVAR5=", "ENVVAR4",
"ENVVAR6= ", "ENVVAR5=",
"ENVVAR7=ENVVAR8=ENVVAR9", "ENVVAR6= ",
"PATH=/bin:/sbin", "ENVVAR7=ENVVAR8=ENVVAR9",
}, "PATH=/bin:/sbin",
},
ContainerJSONBase: &types.ContainerJSONBase{
State: &types.ContainerState{
Health: &types.Health{
FailingStreak: 1,
Status: "Unhealthy",
}, },
Status: "running",
OOMKilled: false,
Pid: 1234,
ExitCode: 0,
StartedAt: "2018-06-14T05:48:53.266176036Z",
FinishedAt: "0001-01-01T00:00:00Z",
}, },
}, ContainerJSONBase: &types.ContainerJSONBase{
State: &types.ContainerState{
Health: &types.Health{
FailingStreak: 1,
Status: "Unhealthy",
},
Status: "running",
OOMKilled: false,
Pid: 1234,
ExitCode: 0,
StartedAt: "2018-06-14T05:48:53.266176036Z",
FinishedAt: "0001-01-01T00:00:00Z",
},
},
}
} }