Fix uptime_ns calculation when container has been restarted (#6649)
This commit is contained in:
parent
d858d82a85
commit
ce3ae58ad9
|
@ -546,17 +546,22 @@ func (d *Docker) gatherContainerInspect(
|
||||||
started, err := time.Parse(time.RFC3339, info.State.StartedAt)
|
started, err := time.Parse(time.RFC3339, info.State.StartedAt)
|
||||||
if err == nil && !started.IsZero() {
|
if err == nil && !started.IsZero() {
|
||||||
statefields["started_at"] = started.UnixNano()
|
statefields["started_at"] = started.UnixNano()
|
||||||
statefields["uptime_ns"] = finished.Sub(started).Nanoseconds()
|
|
||||||
|
uptime := finished.Sub(started)
|
||||||
|
if finished.Before(started) {
|
||||||
|
uptime = now().Sub(started)
|
||||||
|
}
|
||||||
|
statefields["uptime_ns"] = uptime.Nanoseconds()
|
||||||
}
|
}
|
||||||
|
|
||||||
acc.AddFields("docker_container_status", statefields, tags, time.Now())
|
acc.AddFields("docker_container_status", statefields, tags, now())
|
||||||
|
|
||||||
if info.State.Health != nil {
|
if info.State.Health != nil {
|
||||||
healthfields := map[string]interface{}{
|
healthfields := map[string]interface{}{
|
||||||
"health_status": info.State.Health.Status,
|
"health_status": info.State.Health.Status,
|
||||||
"failing_streak": info.ContainerJSONBase.State.Health.FailingStreak,
|
"failing_streak": info.ContainerJSONBase.State.Health.FailingStreak,
|
||||||
}
|
}
|
||||||
acc.AddFields("docker_container_health", healthfields, tags, time.Now())
|
acc.AddFields("docker_container_health", healthfields, tags, now())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -541,25 +542,22 @@ func TestContainerNames(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerStatus(t *testing.T) {
|
func FilterMetrics(metrics []telegraf.Metric, f func(telegraf.Metric) bool) []telegraf.Metric {
|
||||||
type expectation struct {
|
results := []telegraf.Metric{}
|
||||||
// tags
|
for _, m := range metrics {
|
||||||
Status string
|
if f(m) {
|
||||||
// fields
|
results = append(results, m)
|
||||||
ContainerID string
|
|
||||||
OOMKilled bool
|
|
||||||
Pid int
|
|
||||||
ExitCode int
|
|
||||||
StartedAt time.Time
|
|
||||||
FinishedAt time.Time
|
|
||||||
UptimeNs int64
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContainerStatus(t *testing.T) {
|
||||||
var tests = []struct {
|
var tests = []struct {
|
||||||
name string
|
name string
|
||||||
now func() time.Time
|
now func() time.Time
|
||||||
inspect types.ContainerJSON
|
inspect types.ContainerJSON
|
||||||
expect expectation
|
expected []telegraf.Metric
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "finished_at is zero value",
|
name: "finished_at is zero value",
|
||||||
|
@ -567,49 +565,141 @@ func TestContainerStatus(t *testing.T) {
|
||||||
return time.Date(2018, 6, 14, 5, 51, 53, 266176036, time.UTC)
|
return time.Date(2018, 6, 14, 5, 51, 53, 266176036, time.UTC)
|
||||||
},
|
},
|
||||||
inspect: containerInspect(),
|
inspect: containerInspect(),
|
||||||
expect: expectation{
|
expected: []telegraf.Metric{
|
||||||
ContainerID: "e2173b9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296b7dfb",
|
testutil.MustMetric(
|
||||||
Status: "running",
|
"docker_container_status",
|
||||||
OOMKilled: false,
|
map[string]string{
|
||||||
Pid: 1234,
|
"container_name": "etcd",
|
||||||
ExitCode: 0,
|
"container_image": "quay.io/coreos/etcd",
|
||||||
StartedAt: time.Date(2018, 6, 14, 5, 48, 53, 266176036, time.UTC),
|
"container_version": "v2.2.2",
|
||||||
UptimeNs: int64(3 * time.Minute),
|
"engine_host": "absol",
|
||||||
|
"label1": "test_value_1",
|
||||||
|
"label2": "test_value_2",
|
||||||
|
"server_version": "17.09.0-ce",
|
||||||
|
"container_status": "running",
|
||||||
|
"source": "e2173b9478a6",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"oomkilled": false,
|
||||||
|
"pid": 1234,
|
||||||
|
"exitcode": 0,
|
||||||
|
"container_id": "e2173b9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296b7dfb",
|
||||||
|
"started_at": time.Date(2018, 6, 14, 5, 48, 53, 266176036, time.UTC).UnixNano(),
|
||||||
|
"uptime_ns": int64(3 * time.Minute),
|
||||||
|
},
|
||||||
|
time.Date(2018, 6, 14, 5, 51, 53, 266176036, time.UTC),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "finished_at is non-zero value",
|
name: "finished_at is non-zero value",
|
||||||
|
now: func() time.Time {
|
||||||
|
return time.Date(2018, 6, 14, 5, 51, 53, 266176036, time.UTC)
|
||||||
|
},
|
||||||
inspect: func() types.ContainerJSON {
|
inspect: func() types.ContainerJSON {
|
||||||
i := containerInspect()
|
i := containerInspect()
|
||||||
i.ContainerJSONBase.State.FinishedAt = "2018-06-14T05:53:53.266176036Z"
|
i.ContainerJSONBase.State.FinishedAt = "2018-06-14T05:53:53.266176036Z"
|
||||||
return i
|
return i
|
||||||
}(),
|
}(),
|
||||||
expect: expectation{
|
expected: []telegraf.Metric{
|
||||||
ContainerID: "e2173b9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296b7dfb",
|
testutil.MustMetric(
|
||||||
Status: "running",
|
"docker_container_status",
|
||||||
OOMKilled: false,
|
map[string]string{
|
||||||
Pid: 1234,
|
"container_name": "etcd",
|
||||||
ExitCode: 0,
|
"container_image": "quay.io/coreos/etcd",
|
||||||
StartedAt: time.Date(2018, 6, 14, 5, 48, 53, 266176036, time.UTC),
|
"container_version": "v2.2.2",
|
||||||
FinishedAt: time.Date(2018, 6, 14, 5, 53, 53, 266176036, time.UTC),
|
"engine_host": "absol",
|
||||||
UptimeNs: int64(5 * time.Minute),
|
"label1": "test_value_1",
|
||||||
|
"label2": "test_value_2",
|
||||||
|
"server_version": "17.09.0-ce",
|
||||||
|
"container_status": "running",
|
||||||
|
"source": "e2173b9478a6",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"oomkilled": false,
|
||||||
|
"pid": 1234,
|
||||||
|
"exitcode": 0,
|
||||||
|
"container_id": "e2173b9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296b7dfb",
|
||||||
|
"started_at": time.Date(2018, 6, 14, 5, 48, 53, 266176036, time.UTC).UnixNano(),
|
||||||
|
"finished_at": time.Date(2018, 6, 14, 5, 53, 53, 266176036, time.UTC).UnixNano(),
|
||||||
|
"uptime_ns": int64(5 * time.Minute),
|
||||||
|
},
|
||||||
|
time.Date(2018, 6, 14, 5, 51, 53, 266176036, time.UTC),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "started_at is zero value",
|
name: "started_at is zero value",
|
||||||
|
now: func() time.Time {
|
||||||
|
return time.Date(2018, 6, 14, 5, 51, 53, 266176036, time.UTC)
|
||||||
|
},
|
||||||
inspect: func() types.ContainerJSON {
|
inspect: func() types.ContainerJSON {
|
||||||
i := containerInspect()
|
i := containerInspect()
|
||||||
i.ContainerJSONBase.State.StartedAt = ""
|
i.ContainerJSONBase.State.StartedAt = ""
|
||||||
i.ContainerJSONBase.State.FinishedAt = "2018-06-14T05:53:53.266176036Z"
|
i.ContainerJSONBase.State.FinishedAt = "2018-06-14T05:53:53.266176036Z"
|
||||||
return i
|
return i
|
||||||
}(),
|
}(),
|
||||||
expect: expectation{
|
expected: []telegraf.Metric{
|
||||||
ContainerID: "e2173b9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296b7dfb",
|
testutil.MustMetric(
|
||||||
Status: "running",
|
"docker_container_status",
|
||||||
OOMKilled: false,
|
map[string]string{
|
||||||
Pid: 1234,
|
"container_name": "etcd",
|
||||||
ExitCode: 0,
|
"container_image": "quay.io/coreos/etcd",
|
||||||
FinishedAt: time.Date(2018, 6, 14, 5, 53, 53, 266176036, time.UTC),
|
"container_version": "v2.2.2",
|
||||||
|
"engine_host": "absol",
|
||||||
|
"label1": "test_value_1",
|
||||||
|
"label2": "test_value_2",
|
||||||
|
"server_version": "17.09.0-ce",
|
||||||
|
"container_status": "running",
|
||||||
|
"source": "e2173b9478a6",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"oomkilled": false,
|
||||||
|
"pid": 1234,
|
||||||
|
"exitcode": 0,
|
||||||
|
"container_id": "e2173b9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296b7dfb",
|
||||||
|
"finished_at": time.Date(2018, 6, 14, 5, 53, 53, 266176036, time.UTC).UnixNano(),
|
||||||
|
},
|
||||||
|
time.Date(2018, 6, 14, 5, 51, 53, 266176036, time.UTC),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "container has been restarted",
|
||||||
|
now: func() time.Time {
|
||||||
|
return time.Date(2019, 1, 1, 0, 0, 3, 0, time.UTC)
|
||||||
|
},
|
||||||
|
inspect: func() types.ContainerJSON {
|
||||||
|
i := containerInspect()
|
||||||
|
i.ContainerJSONBase.State.StartedAt = "2019-01-01T00:00:02Z"
|
||||||
|
i.ContainerJSONBase.State.FinishedAt = "2019-01-01T00:00:01Z"
|
||||||
|
return i
|
||||||
|
}(),
|
||||||
|
expected: []telegraf.Metric{
|
||||||
|
testutil.MustMetric(
|
||||||
|
"docker_container_status",
|
||||||
|
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": "running",
|
||||||
|
"source": "e2173b9478a6",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"oomkilled": false,
|
||||||
|
"pid": 1234,
|
||||||
|
"exitcode": 0,
|
||||||
|
"container_id": "e2173b9478a6ae55e237d4d74f8bbb753f0817192b5081334dc78476296b7dfb",
|
||||||
|
"started_at": time.Date(2019, 1, 1, 0, 0, 2, 0, time.UTC).UnixNano(),
|
||||||
|
"finished_at": time.Date(2019, 1, 1, 0, 0, 1, 0, time.UTC).UnixNano(),
|
||||||
|
"uptime_ns": int64(1 * time.Second),
|
||||||
|
},
|
||||||
|
time.Date(2019, 1, 1, 0, 0, 3, 0, time.UTC),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -643,39 +733,13 @@ func TestContainerStatus(t *testing.T) {
|
||||||
now = time.Now
|
now = time.Now
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := acc.GatherError(d.Gather)
|
err := d.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
actual := FilterMetrics(acc.GetTelegrafMetrics(), func(m telegraf.Metric) bool {
|
||||||
"oomkilled": tt.expect.OOMKilled,
|
return m.Name() == "docker_container_status"
|
||||||
"pid": tt.expect.Pid,
|
|
||||||
"exitcode": tt.expect.ExitCode,
|
|
||||||
"container_id": tt.expect.ContainerID,
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
"source": "e2173b9478a6",
|
|
||||||
})
|
})
|
||||||
|
testutil.RequireMetricsEqual(t, tt.expected, actual)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue