diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fbcaf018da18..5bf71afedcc66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ in conjunction with wildcard dimension values as it will control the amount of time before a new metric is included by the plugin. ### Features - +- [#1294](https://github.com/influxdata/telegraf/pull/1294): consul input plugin. Thanks @harnash - [#1164](https://github.com/influxdata/telegraf/pull/1164): conntrack input plugin. Thanks @robinpercy! - [#1165](https://github.com/influxdata/telegraf/pull/1165): vmstat input plugin. Thanks @jshim-xm! - [#1247](https://github.com/influxdata/telegraf/pull/1247): rollbar input plugin. Thanks @francois2metz and @cduez! diff --git a/Godeps b/Godeps index ed28e1470d6d5..05bd7ef16d6f5 100644 --- a/Godeps +++ b/Godeps @@ -23,6 +23,7 @@ github.com/gonuts/go-shellquote e842a11b24c6abfb3dd27af69a17f482e4b483c2 github.com/gorilla/context 1ea25387ff6f684839d82767c1733ff4d4d15d0a github.com/gorilla/mux c9e326e2bdec29039a3761c07bece13133863e1e github.com/hailocab/go-hostpool e80d13ce29ede4452c43dea11e79b9bc8a15b478 +github.com/hashicorp/consul 5aa90455ce78d4d41578bafc86305e6e6b28d7d2 github.com/hpcloud/tail b2940955ab8b26e19d43a43c4da0475dd81bdb56 github.com/influxdata/config b79f6829346b8d6e78ba73544b1e1038f1f1c9da github.com/influxdata/influxdb e094138084855d444195b252314dfee9eae34cab diff --git a/README.md b/README.md index 7366d5986656c..fdd750f2c3f4a 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,7 @@ Currently implemented sources: * [cassandra](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/cassandra) * [ceph](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/ceph) * [chrony](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/chrony) +* [consul](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/consul) * [conntrack](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/conntrack) * [couchbase](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/couchbase) * [couchdb](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/couchdb) diff --git a/plugins/inputs/all/all.go b/plugins/inputs/all/all.go index 8c12e08589bd9..0dbbb613d1d5d 100644 --- a/plugins/inputs/all/all.go +++ b/plugins/inputs/all/all.go @@ -9,6 +9,7 @@ import ( _ "github.com/influxdata/telegraf/plugins/inputs/chrony" _ "github.com/influxdata/telegraf/plugins/inputs/cloudwatch" _ "github.com/influxdata/telegraf/plugins/inputs/conntrack" + _ "github.com/influxdata/telegraf/plugins/inputs/consul" _ "github.com/influxdata/telegraf/plugins/inputs/couchbase" _ "github.com/influxdata/telegraf/plugins/inputs/couchdb" _ "github.com/influxdata/telegraf/plugins/inputs/disque" diff --git a/plugins/inputs/consul/README.md b/plugins/inputs/consul/README.md new file mode 100644 index 0000000000000..a2685e2bf304f --- /dev/null +++ b/plugins/inputs/consul/README.md @@ -0,0 +1,46 @@ +# Telegraf Input Plugin: Consul + +This plugin will collect statistics about all helath checks registered in the Consul. It uses [Consul API](https://www.consul.io/docs/agent/http/health.html#health_state) +to query the data. It will not report the [telemetry](https://www.consul.io/docs/agent/telemetry.html) but Consul can report those stats already using StatsD protocol if needed. + +## Configuration: + +``` +# Gather health check statuses from services registered in Consul +[[inputs.consul]] + ## Most of these values defaults to the one configured on a Consul's agent level. + ## Optional Consul server address (default: "") + # address = "" + ## Optional URI scheme for the Consul server (default: "") + # scheme = "" + ## Optional ACL token used in every request (default: "") + # token = "" + ## Optional username used for request HTTP Basic Authentication (default: "") + # username = "" + ## Optional password used for HTTP Basic Authentication (default: "") + # password = "" + ## Optional data centre to query the health checks from (default: "") + # datacentre = "" +``` + +## Measurements: + +### Consul: +Tags: +- node: on which node check/service is registered on +- service_name: name of the service (this is the service name not the service ID) + +Fields: +- check_id +- check_name +- service_id +- status + +## Example output + +``` +$ telegraf --config ./telegraf.conf -input-filter consul -test +* Plugin: consul, Collection 1 +> consul_health_checks,host=wolfpit,node=consul-server-node check_id="serfHealth",check_name="Serf Health Status",service_id="",status="passing" 1464698464486439902 +> consul_health_checks,host=wolfpit,node=consul-server-node,service_name=www.example.com check_id="service:www-example-com.test01",check_name="Service 'www.example.com' check",service_id="www-example-com.test01",status="critical" 1464698464486519036 +``` diff --git a/plugins/inputs/consul/consul.go b/plugins/inputs/consul/consul.go new file mode 100644 index 0000000000000..eaeae73c168af --- /dev/null +++ b/plugins/inputs/consul/consul.go @@ -0,0 +1,136 @@ +package consul + +import ( + "net/http" + + "github.com/hashicorp/consul/api" + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/internal" + "github.com/influxdata/telegraf/plugins/inputs" +) + +type Consul struct { + Address string + Scheme string + Token string + Username string + Password string + Datacentre string + + // Path to CA file + SSLCA string `toml:"ssl_ca"` + // Path to host cert file + SSLCert string `toml:"ssl_cert"` + // Path to cert key file + SSLKey string `toml:"ssl_key"` + // Use SSL but skip chain & host verification + InsecureSkipVerify bool + + // client used to connect to Consul agnet + client *api.Client +} + +var sampleConfig = ` + ## Most of these values defaults to the one configured on a Consul's agent level. + ## Optional Consul server address (default: "localhost") + # address = "localhost" + ## Optional URI scheme for the Consul server (default: "http") + # scheme = "http" + ## Optional ACL token used in every request (default: "") + # token = "" + ## Optional username used for request HTTP Basic Authentication (default: "") + # username = "" + ## Optional password used for HTTP Basic Authentication (default: "") + # password = "" + ## Optional data centre to query the health checks from (default: "") + # datacentre = "" +` + +func (c *Consul) Description() string { + return "Gather health check statuses from services registered in Consul" +} + +func (c *Consul) SampleConfig() string { + return sampleConfig +} + +func (c *Consul) createAPIClient() (*api.Client, error) { + config := api.DefaultConfig() + + if c.Address != "" { + config.Address = c.Address + } + + if c.Scheme != "" { + config.Scheme = c.Scheme + } + + if c.Datacentre != "" { + config.Datacenter = c.Datacentre + } + + if c.Username != "" { + config.HttpAuth = &api.HttpBasicAuth{ + Username: c.Username, + Password: c.Password, + } + } + + tlsCfg, err := internal.GetTLSConfig( + c.SSLCert, c.SSLKey, c.SSLCA, c.InsecureSkipVerify) + + if err != nil { + return nil, err + } + + config.HttpClient.Transport = &http.Transport{ + TLSClientConfig: tlsCfg, + } + + return api.NewClient(config) +} + +func (c *Consul) GatherHealthCheck(acc telegraf.Accumulator, checks []*api.HealthCheck) { + for _, check := range checks { + record := make(map[string]interface{}) + tags := make(map[string]string) + + record["check_id"] = check.CheckID + record["check_name"] = check.Name + record["service_id"] = check.ServiceID + record["status"] = check.Status + + tags["node"] = check.Node + tags["service_name"] = check.ServiceName + + acc.AddFields("consul_health_checks", record, tags) + } +} + +func (c *Consul) Gather(acc telegraf.Accumulator) error { + if c.client == nil { + newClient, err := c.createAPIClient() + + if err != nil { + return err + } + + c.client = newClient + } + + checks, _, err := c.client.Health().State("any", nil) + + if err != nil { + return err + } + + c.GatherHealthCheck(acc, checks) + + return nil +} + +func init() { + inputs.Add("consul", func() telegraf.Input { + return &Consul{} + }) +} diff --git a/plugins/inputs/consul/consul_test.go b/plugins/inputs/consul/consul_test.go new file mode 100644 index 0000000000000..772ccba911e3a --- /dev/null +++ b/plugins/inputs/consul/consul_test.go @@ -0,0 +1,42 @@ +package consul + +import ( + "testing" + + "github.com/hashicorp/consul/api" + "github.com/influxdata/telegraf/testutil" +) + +var sampleChecks = []*api.HealthCheck{ + &api.HealthCheck{ + Node: "localhost", + CheckID: "foo.health123", + Name: "foo.health", + Status: "passing", + Notes: "lorem ipsum", + Output: "OK", + ServiceID: "foo.123", + ServiceName: "foo", + }, +} + +func TestGatherHealtCheck(t *testing.T) { + expectedFields := map[string]interface{}{ + "check_id": "foo.health123", + "check_name": "foo.health", + "status": "passing", + "service_id": "foo.123", + } + + expectedTags := map[string]string{ + "node": "localhost", + "service_name": "foo", + } + + var acc testutil.Accumulator + + consul := &Consul{} + consul.GatherHealthCheck(&acc, sampleChecks) + + acc.AssertContainsTaggedFields(t, "consul_health_checks", expectedFields, expectedTags) +}