From cd0cebcabd59c9b54aae62ae848cd745b57e7f5c Mon Sep 17 00:00:00 2001 From: sebastienmusso Date: Fri, 20 Jan 2017 11:36:27 +0100 Subject: [PATCH] add healthcheck metricset in docker module --- .../docs/modules/docker/healthcheck.asciidoc | 19 +++++++ .../module/docker/healthcheck/_meta/data.json | 24 +++++++++ .../docker/healthcheck/_meta/docs.asciidoc | 4 ++ .../docker/healthcheck/_meta/fields.yml | 29 +++++++++++ .../healthcheck/container_integration_test.go | 25 +++++++++ metricbeat/module/docker/healthcheck/data.go | 50 ++++++++++++++++++ .../module/docker/healthcheck/healthcheck.go | 52 +++++++++++++++++++ 7 files changed, 203 insertions(+) create mode 100644 metricbeat/docs/modules/docker/healthcheck.asciidoc create mode 100644 metricbeat/module/docker/healthcheck/_meta/data.json create mode 100644 metricbeat/module/docker/healthcheck/_meta/docs.asciidoc create mode 100644 metricbeat/module/docker/healthcheck/_meta/fields.yml create mode 100644 metricbeat/module/docker/healthcheck/container_integration_test.go create mode 100644 metricbeat/module/docker/healthcheck/data.go create mode 100644 metricbeat/module/docker/healthcheck/healthcheck.go diff --git a/metricbeat/docs/modules/docker/healthcheck.asciidoc b/metricbeat/docs/modules/docker/healthcheck.asciidoc new file mode 100644 index 000000000000..220ba07630d9 --- /dev/null +++ b/metricbeat/docs/modules/docker/healthcheck.asciidoc @@ -0,0 +1,19 @@ +//// +This file is generated! See scripts/docs_collector.py +//// + +[[metricbeat-metricset-docker-healthcheck]] +include::../../../module/docker/healthcheck/_meta/docs.asciidoc[] + + +==== Fields + +For a description of each field in the metricset, see the +<> section. + +Here is an example document generated by this metricset: + +[source,json] +---- +include::../../../module/docker/healthcheck/_meta/data.json[] +---- diff --git a/metricbeat/module/docker/healthcheck/_meta/data.json b/metricbeat/module/docker/healthcheck/_meta/data.json new file mode 100644 index 000000000000..16e91a84feba --- /dev/null +++ b/metricbeat/module/docker/healthcheck/_meta/data.json @@ -0,0 +1,24 @@ +{ + "@timestamp": "2016-05-23T08:05:34.853Z", + "beat": { + "hostname": "host.example.com", + "name": "host.example.com" + }, + "docker": { + "healthcheck": { + "event_end_date": "2017-01-09T20:38:13.080472813+01:00", + "event_exit_code": 0, + "event_output": "this is an event output", + "event_start_date": "2017-01-09T20:38:12.999970865+01:00", + "failingstreak": 0, + "status": "healthy" + } + }, + "metricset": { + "host": "/var/run/docker.sock", + "module": "docker", + "name": "container", + "rtt": 115 + }, + "type": "metricsets" +} diff --git a/metricbeat/module/docker/healthcheck/_meta/docs.asciidoc b/metricbeat/module/docker/healthcheck/_meta/docs.asciidoc new file mode 100644 index 000000000000..3c095e4e9c0e --- /dev/null +++ b/metricbeat/module/docker/healthcheck/_meta/docs.asciidoc @@ -0,0 +1,4 @@ +=== Docker Container Metricset + +The Docker `container` metricset collects information and statistics about +running Docker containers. diff --git a/metricbeat/module/docker/healthcheck/_meta/fields.yml b/metricbeat/module/docker/healthcheck/_meta/fields.yml new file mode 100644 index 000000000000..4cd5c8dc5ba2 --- /dev/null +++ b/metricbeat/module/docker/healthcheck/_meta/fields.yml @@ -0,0 +1,29 @@ +- name: healthcheck + type: group + description: > + Docker container metrics. + fields: + - name: event_end_date + type: date + description: > + Healthcheck end date + - name: event_start_date + type: date + description: > + Healthcheck start date + - name: event_output + type: keyword + description: > + Healthcheck output + - name: event_exit_code + type: integer + description: > + Healthcheck status code + - name: failingstreak + type: integer + description: > + concurent failed check + - name: status + type: keyword + description: > + Healthcheck status code diff --git a/metricbeat/module/docker/healthcheck/container_integration_test.go b/metricbeat/module/docker/healthcheck/container_integration_test.go new file mode 100644 index 000000000000..8c1356f7366c --- /dev/null +++ b/metricbeat/module/docker/healthcheck/container_integration_test.go @@ -0,0 +1,25 @@ +// +build integration + +package healthcheck + +import ( + "testing" + + mbtest "github.com/elastic/beats/metricbeat/mb/testing" +) + +func TestData(t *testing.T) { + f := mbtest.NewEventsFetcher(t, getConfig()) + err := mbtest.WriteEvents(f, t) + if err != nil { + t.Fatal("write", err) + } +} + +func getConfig() map[string]interface{} { + return map[string]interface{}{ + "module": "docker", + "metricsets": []string{"healthcheck"}, + "hosts": []string{"unix:///var/run/docker.sock"}, + } +} diff --git a/metricbeat/module/docker/healthcheck/data.go b/metricbeat/module/docker/healthcheck/data.go new file mode 100644 index 000000000000..d436092c2cb8 --- /dev/null +++ b/metricbeat/module/docker/healthcheck/data.go @@ -0,0 +1,50 @@ +package healthcheck + +import ( + //"time" + + "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/metricbeat/mb" + "github.com/elastic/beats/metricbeat/module/docker" + + dc "github.com/fsouza/go-dockerclient" + "reflect" + "strings" +) + +func eventsMapping(containersList []dc.APIContainers, m *MetricSet) []common.MapStr { + myEvents := []common.MapStr{} + emptyEvent := common.MapStr{} + for _, container := range containersList { + returnevent := eventMapping(&container, m) + if !reflect.DeepEqual(emptyEvent, returnevent) { + myEvents = append(myEvents, eventMapping(&container, m)) + } + } + return myEvents +} + +func eventMapping(cont *dc.APIContainers, m *MetricSet) common.MapStr { + event := common.MapStr{} + if strings.Contains(cont.Status, "(") && strings.Contains(cont.Status, ")") { + container, _ := m.dockerClient.InspectContainer(cont.ID) + last_event := len(container.State.Health.Log) - 1 + if last_event >= 0 { + event = common.MapStr{ + mb.ModuleData: common.MapStr{ + "container": common.MapStr{ + "name": docker.ExtractContainerName(cont.Names), + }, + }, + "status": container.State.Health.Status, + "failingstreak": container.State.Health.FailingStreak, + "event_start_date": common.Time(container.State.Health.Log[last_event].Start), + "event_end_date": common.Time(container.State.Health.Log[last_event].End), + "event_exit_code": container.State.Health.Log[last_event].ExitCode, + "event_output": container.State.Health.Log[last_event].Output, + } + } + } + + return event +} diff --git a/metricbeat/module/docker/healthcheck/healthcheck.go b/metricbeat/module/docker/healthcheck/healthcheck.go new file mode 100644 index 000000000000..0c775056e105 --- /dev/null +++ b/metricbeat/module/docker/healthcheck/healthcheck.go @@ -0,0 +1,52 @@ +package healthcheck + +import ( + dc "github.com/fsouza/go-dockerclient" + + "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/logp" + "github.com/elastic/beats/metricbeat/mb" + "github.com/elastic/beats/metricbeat/module/docker" +) + +func init() { + if err := mb.Registry.AddMetricSet("docker", "healthcheck", New, docker.HostParser); err != nil { + panic(err) + } +} + +type MetricSet struct { + mb.BaseMetricSet + dockerClient *dc.Client +} + +// New creates a new instance of the docker container MetricSet. +func New(base mb.BaseMetricSet) (mb.MetricSet, error) { + logp.Warn("EXPERIMENTAL: The docker healthcheck metricset is experimental") + + config := docker.Config{} + if err := base.Module().UnpackConfig(&config); err != nil { + return nil, err + } + + client, err := docker.NewDockerClient(base.HostData().URI, config) + if err != nil { + return nil, err + } + + return &MetricSet{ + BaseMetricSet: base, + dockerClient: client, + }, nil +} + +// Fetch returns a list of all containers as events. +// This is based on https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/list-containers. +func (m *MetricSet) Fetch() ([]common.MapStr, error) { + // Fetch a list of all containers. + containers, err := m.dockerClient.ListContainers(dc.ListContainersOptions{}) + if err != nil { + return nil, err + } + return eventsMapping(containers, m), nil +}