Skip to content

Commit

Permalink
Add container uptime_ns in docker input plugin (influxdata#5996)
Browse files Browse the repository at this point in the history
  • Loading branch information
GeorgeMac authored and bitcharmer committed Oct 18, 2019
1 parent 49695ff commit cec14e4
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 33 deletions.
1 change: 1 addition & 0 deletions plugins/inputs/docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ status if configured.
- exitcode (integer)
- started_at (integer)
- finished_at (integer)
- uptime_ns (integer)

- docker_swarm
- tags:
Expand Down
20 changes: 14 additions & 6 deletions plugins/inputs/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const (
var (
sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[bB]?$`)
containerStates = []string{"created", "restarting", "running", "removing", "paused", "exited", "dead"}
now = time.Now
)

var sampleConfig = `
Expand Down Expand Up @@ -462,14 +463,21 @@ func (d *Docker) gatherContainer(
"pid": info.State.Pid,
"exitcode": info.State.ExitCode,
}
container_time, err := time.Parse(time.RFC3339, info.State.StartedAt)
if err == nil && !container_time.IsZero() {
statefields["started_at"] = container_time.UnixNano()

finished, err := time.Parse(time.RFC3339, info.State.FinishedAt)
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() {
statefields["finished_at"] = container_time.UnixNano()

started, err := time.Parse(time.RFC3339, info.State.StartedAt)
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())

if info.State.Health != nil {
Expand Down
134 changes: 132 additions & 2 deletions plugins/inputs/docker/docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"sort"
"strings"
"testing"
"time"

"github.com/influxdata/telegraf/testutil"

Expand Down Expand Up @@ -83,7 +84,7 @@ var baseClient = MockClient{
return containerStats(s), nil
},
ContainerInspectF: func(context.Context, string) (types.ContainerJSON, error) {
return containerInspect, nil
return containerInspect(), nil
},
ServiceListF: func(context.Context, types.ServiceListOptions) ([]swarm.Service, error) {
return ServiceList, nil
Expand Down Expand Up @@ -264,7 +265,7 @@ func TestDocker_WindowsMemoryContainerStats(t *testing.T) {
return containerStatsWindows(), nil
},
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) {
return ServiceList, nil
Expand Down Expand Up @@ -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) {
var acc testutil.Accumulator
d := Docker{
Expand Down
52 changes: 27 additions & 25 deletions plugins/inputs/docker/docker_testdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -492,32 +492,34 @@ func containerStatsWindows() types.ContainerStats {
return stat
}

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

0 comments on commit cec14e4

Please sign in to comment.