mqtt output: cleanup, implement TLS
Also normalize TLS config across all output plugins and normalize comment strings as well.
This commit is contained in:
parent
b941d270ce
commit
bd9c5b6995
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -1,7 +1,22 @@
|
||||||
## v0.10.2 [unreleased]
|
## v0.10.3 [unreleased]
|
||||||
|
|
||||||
### Release Notes
|
### Release Notes
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
## v0.10.2 [2016-02-04]
|
||||||
|
|
||||||
|
### Release Notes
|
||||||
|
- Statsd timing measurements are now aggregated into a single measurement with
|
||||||
|
fields.
|
||||||
|
- Graphite output now inserts tags into the bucket in alphabetical order.
|
||||||
|
- Normalized TLS/SSL support for output plugins: MQTT, AMQP, Kafka
|
||||||
|
- `verify_ssl` config option was removed from Kafka because it was actually
|
||||||
|
doing the opposite of what it claimed to do (yikes). It's been replaced by
|
||||||
|
`insecure_skip_verify`
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
- [#575](https://github.com/influxdata/telegraf/pull/575): Support for collecting Windows Performance Counters. Thanks @TheFlyingCorpse!
|
- [#575](https://github.com/influxdata/telegraf/pull/575): Support for collecting Windows Performance Counters. Thanks @TheFlyingCorpse!
|
||||||
- [#564](https://github.com/influxdata/telegraf/issues/564): features for plugin writing simplification. Internal metric data type.
|
- [#564](https://github.com/influxdata/telegraf/issues/564): features for plugin writing simplification. Internal metric data type.
|
||||||
|
|
|
@ -2,14 +2,20 @@ package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const alphanum string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||||
|
|
||||||
// Duration just wraps time.Duration
|
// Duration just wraps time.Duration
|
||||||
type Duration struct {
|
type Duration struct {
|
||||||
Duration time.Duration
|
Duration time.Duration
|
||||||
|
@ -105,6 +111,57 @@ func ReadLinesOffsetN(filename string, offset uint, n int) ([]string, error) {
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RandomString returns a random string of alpha-numeric characters
|
||||||
|
func RandomString(n int) string {
|
||||||
|
var bytes = make([]byte, n)
|
||||||
|
rand.Read(bytes)
|
||||||
|
for i, b := range bytes {
|
||||||
|
bytes[i] = alphanum[b%byte(len(alphanum))]
|
||||||
|
}
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTLSConfig gets a tls.Config object from the given certs, key, and CA files.
|
||||||
|
// you must give the full path to the files.
|
||||||
|
// If all files are blank and InsecureSkipVerify=false, returns a nil pointer.
|
||||||
|
func GetTLSConfig(
|
||||||
|
SSLCert, SSLKey, SSLCA string,
|
||||||
|
InsecureSkipVerify bool,
|
||||||
|
) (*tls.Config, error) {
|
||||||
|
t := &tls.Config{}
|
||||||
|
if SSLCert != "" && SSLKey != "" && SSLCA != "" {
|
||||||
|
cert, err := tls.LoadX509KeyPair(SSLCert, SSLKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(fmt.Sprintf(
|
||||||
|
"Could not load TLS client key/certificate: %s",
|
||||||
|
err))
|
||||||
|
}
|
||||||
|
|
||||||
|
caCert, err := ioutil.ReadFile(SSLCA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(fmt.Sprintf("Could not load TLS CA: %s",
|
||||||
|
err))
|
||||||
|
}
|
||||||
|
|
||||||
|
caCertPool := x509.NewCertPool()
|
||||||
|
caCertPool.AppendCertsFromPEM(caCert)
|
||||||
|
|
||||||
|
t = &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{cert},
|
||||||
|
RootCAs: caCertPool,
|
||||||
|
InsecureSkipVerify: InsecureSkipVerify,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if InsecureSkipVerify {
|
||||||
|
t.InsecureSkipVerify = true
|
||||||
|
} else {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// will be nil by default if nothing is provided
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Glob will test a string pattern, potentially containing globs, against a
|
// Glob will test a string pattern, potentially containing globs, against a
|
||||||
// subject string. The result is a simple true/false, determining whether or
|
// subject string. The result is a simple true/false, determining whether or
|
||||||
// not the glob pattern matched the subject text.
|
// not the glob pattern matched the subject text.
|
||||||
|
|
|
@ -22,13 +22,13 @@ type Amon struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
# Amon Server Key
|
### Amon Server Key
|
||||||
server_key = "my-server-key" # required.
|
server_key = "my-server-key" # required.
|
||||||
|
|
||||||
# Amon Instance URL
|
### Amon Instance URL
|
||||||
amon_instance = "https://youramoninstance" # required
|
amon_instance = "https://youramoninstance" # required
|
||||||
|
|
||||||
# Connection timeout.
|
### Connection timeout.
|
||||||
# timeout = "5s"
|
# timeout = "5s"
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,13 @@ package amqp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/internal"
|
||||||
"github.com/influxdata/telegraf/plugins/outputs"
|
"github.com/influxdata/telegraf/plugins/outputs"
|
||||||
"github.com/streadway/amqp"
|
"github.com/streadway/amqp"
|
||||||
)
|
)
|
||||||
|
@ -20,12 +18,6 @@ type AMQP struct {
|
||||||
URL string
|
URL string
|
||||||
// AMQP exchange
|
// AMQP exchange
|
||||||
Exchange string
|
Exchange string
|
||||||
// path to CA file
|
|
||||||
SslCa string
|
|
||||||
// path to host cert file
|
|
||||||
SslCert string
|
|
||||||
// path to cert key file
|
|
||||||
SslKey string
|
|
||||||
// Routing Key Tag
|
// Routing Key Tag
|
||||||
RoutingTag string `toml:"routing_tag"`
|
RoutingTag string `toml:"routing_tag"`
|
||||||
// InfluxDB database
|
// InfluxDB database
|
||||||
|
@ -35,6 +27,15 @@ type AMQP struct {
|
||||||
// InfluxDB precision
|
// InfluxDB precision
|
||||||
Precision string
|
Precision 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
|
||||||
|
|
||||||
channel *amqp.Channel
|
channel *amqp.Channel
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
headers amqp.Table
|
headers amqp.Table
|
||||||
|
@ -47,25 +48,27 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
# AMQP url
|
### AMQP url
|
||||||
url = "amqp://localhost:5672/influxdb"
|
url = "amqp://localhost:5672/influxdb"
|
||||||
# AMQP exchange
|
### AMQP exchange
|
||||||
exchange = "telegraf"
|
exchange = "telegraf"
|
||||||
# Telegraf tag to use as a routing key
|
### Telegraf tag to use as a routing key
|
||||||
# ie, if this tag exists, it's value will be used as the routing key
|
### ie, if this tag exists, it's value will be used as the routing key
|
||||||
routing_tag = "host"
|
routing_tag = "host"
|
||||||
|
|
||||||
# Use ssl
|
### InfluxDB retention policy
|
||||||
|
# retention_policy = "default"
|
||||||
|
### InfluxDB database
|
||||||
|
# database = "telegraf"
|
||||||
|
### InfluxDB precision
|
||||||
|
# precision = "s"
|
||||||
|
|
||||||
|
### Optional SSL Config
|
||||||
# ssl_ca = "/etc/telegraf/ca.pem"
|
# ssl_ca = "/etc/telegraf/ca.pem"
|
||||||
# ssl_cert = "/etc/telegraf/cert.pem"
|
# ssl_cert = "/etc/telegraf/cert.pem"
|
||||||
# ssl_key = "/etc/telegraf/key.pem"
|
# ssl_key = "/etc/telegraf/key.pem"
|
||||||
|
### Use SSL but skip chain & host verification
|
||||||
# InfluxDB retention policy
|
# insecure_skip_verify = false
|
||||||
#retention_policy = "default"
|
|
||||||
# InfluxDB database
|
|
||||||
#database = "telegraf"
|
|
||||||
# InfluxDB precision
|
|
||||||
#precision = "s"
|
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *AMQP) Connect() error {
|
func (q *AMQP) Connect() error {
|
||||||
|
@ -79,28 +82,15 @@ func (q *AMQP) Connect() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var connection *amqp.Connection
|
var connection *amqp.Connection
|
||||||
var err error
|
|
||||||
if q.SslCert != "" && q.SslKey != "" {
|
|
||||||
// make new tls config
|
// make new tls config
|
||||||
cfg := new(tls.Config)
|
tls, err := internal.GetTLSConfig(
|
||||||
if q.SslCa != "" {
|
q.SSLCert, q.SSLKey, q.SSLCA, q.InsecureSkipVerify)
|
||||||
// create ca pool
|
if err != nil {
|
||||||
cfg.RootCAs = x509.NewCertPool()
|
return err
|
||||||
|
}
|
||||||
// add self-signed cert
|
|
||||||
if ca, err := ioutil.ReadFile(q.SslCa); err == nil {
|
|
||||||
cfg.RootCAs.AppendCertsFromPEM(ca)
|
|
||||||
} else {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if cert, err := tls.LoadX509KeyPair(q.SslCert, q.SslKey); err == nil {
|
|
||||||
cfg.Certificates = append(cfg.Certificates, cert)
|
|
||||||
} else {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
connection, err = amqp.DialTLS(q.URL, cfg)
|
|
||||||
|
|
||||||
|
if tls != nil {
|
||||||
|
connection, err = amqp.DialTLS(q.URL, tls)
|
||||||
} else {
|
} else {
|
||||||
connection, err = amqp.Dial(q.URL)
|
connection, err = amqp.Dial(q.URL)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,10 +25,10 @@ type CloudWatch struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
# Amazon REGION
|
### Amazon REGION
|
||||||
region = 'us-east-1'
|
region = 'us-east-1'
|
||||||
|
|
||||||
# Namespace for the CloudWatch MetricDatums
|
### Namespace for the CloudWatch MetricDatums
|
||||||
namespace = 'InfluxData/Telegraf'
|
namespace = 'InfluxData/Telegraf'
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
|
@ -24,10 +24,10 @@ type Datadog struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
# Datadog API key
|
### Datadog API key
|
||||||
apikey = "my-secret-key" # required.
|
apikey = "my-secret-key" # required.
|
||||||
|
|
||||||
# Connection timeout.
|
### Connection timeout.
|
||||||
# timeout = "5s"
|
# timeout = "5s"
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,11 @@ type Graphite struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
# TCP endpoint for your graphite instance.
|
### TCP endpoint for your graphite instance.
|
||||||
servers = ["localhost:2003"]
|
servers = ["localhost:2003"]
|
||||||
# Prefix metrics name
|
### Prefix metrics name
|
||||||
prefix = ""
|
prefix = ""
|
||||||
# timeout in seconds for the write connection to graphite
|
### timeout in seconds for the write connection to graphite
|
||||||
timeout = 2
|
timeout = 2
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
|
@ -32,25 +32,25 @@ type InfluxDB struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
# The full HTTP or UDP endpoint URL for your InfluxDB instance.
|
### The full HTTP or UDP endpoint URL for your InfluxDB instance.
|
||||||
# Multiple urls can be specified but it is assumed that they are part of the same
|
### Multiple urls can be specified but it is assumed that they are part of the same
|
||||||
# cluster, this means that only ONE of the urls will be written to each interval.
|
### cluster, this means that only ONE of the urls will be written to each interval.
|
||||||
# urls = ["udp://localhost:8089"] # UDP endpoint example
|
# urls = ["udp://localhost:8089"] # UDP endpoint example
|
||||||
urls = ["http://localhost:8086"] # required
|
urls = ["http://localhost:8086"] # required
|
||||||
# The target database for metrics (telegraf will create it if not exists)
|
### The target database for metrics (telegraf will create it if not exists)
|
||||||
database = "telegraf" # required
|
database = "telegraf" # required
|
||||||
# Precision of writes, valid values are n, u, ms, s, m, and h
|
### Precision of writes, valid values are n, u, ms, s, m, and h
|
||||||
# note: using second precision greatly helps InfluxDB compression
|
### note: using second precision greatly helps InfluxDB compression
|
||||||
precision = "s"
|
precision = "s"
|
||||||
|
|
||||||
# Connection timeout (for the connection with InfluxDB), formatted as a string.
|
### Connection timeout (for the connection with InfluxDB), formatted as a string.
|
||||||
# If not provided, will default to 0 (no timeout)
|
### If not provided, will default to 0 (no timeout)
|
||||||
# timeout = "5s"
|
# timeout = "5s"
|
||||||
# username = "telegraf"
|
# username = "telegraf"
|
||||||
# password = "metricsmetricsmetricsmetrics"
|
# password = "metricsmetricsmetricsmetrics"
|
||||||
# Set the user agent for HTTP POSTs (can be useful for log differentiation)
|
### Set the user agent for HTTP POSTs (can be useful for log differentiation)
|
||||||
# user_agent = "telegraf"
|
# user_agent = "telegraf"
|
||||||
# Set UDP payload size, defaults to InfluxDB UDP Client default (512 bytes)
|
### Set UDP payload size, defaults to InfluxDB UDP Client default (512 bytes)
|
||||||
# udp_payload = 512
|
# udp_payload = 512
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,14 @@ package kafka
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Shopify/sarama"
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/internal"
|
||||||
"github.com/influxdata/telegraf/plugins/outputs"
|
"github.com/influxdata/telegraf/plugins/outputs"
|
||||||
"io/ioutil"
|
|
||||||
|
"github.com/Shopify/sarama"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Kafka struct {
|
type Kafka struct {
|
||||||
|
@ -18,71 +19,62 @@ type Kafka struct {
|
||||||
Topic string
|
Topic string
|
||||||
// Routing Key Tag
|
// Routing Key Tag
|
||||||
RoutingTag string `toml:"routing_tag"`
|
RoutingTag string `toml:"routing_tag"`
|
||||||
|
|
||||||
|
// Legacy SSL config options
|
||||||
// TLS client certificate
|
// TLS client certificate
|
||||||
Certificate string
|
Certificate string
|
||||||
// TLS client key
|
// TLS client key
|
||||||
Key string
|
Key string
|
||||||
// TLS certificate authority
|
// TLS certificate authority
|
||||||
CA string
|
CA string
|
||||||
// Verfiy SSL certificate chain
|
|
||||||
VerifySsl bool
|
// 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"`
|
||||||
|
|
||||||
|
// Skip SSL verification
|
||||||
|
InsecureSkipVerify bool
|
||||||
|
|
||||||
tlsConfig tls.Config
|
tlsConfig tls.Config
|
||||||
producer sarama.SyncProducer
|
producer sarama.SyncProducer
|
||||||
}
|
}
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
# URLs of kafka brokers
|
### URLs of kafka brokers
|
||||||
brokers = ["localhost:9092"]
|
brokers = ["localhost:9092"]
|
||||||
# Kafka topic for producer messages
|
### Kafka topic for producer messages
|
||||||
topic = "telegraf"
|
topic = "telegraf"
|
||||||
# Telegraf tag to use as a routing key
|
### Telegraf tag to use as a routing key
|
||||||
# ie, if this tag exists, it's value will be used as the routing key
|
### ie, if this tag exists, it's value will be used as the routing key
|
||||||
routing_tag = "host"
|
routing_tag = "host"
|
||||||
|
|
||||||
# Optional TLS configuration:
|
### Optional SSL Config
|
||||||
# Client certificate
|
# ssl_ca = "/etc/telegraf/ca.pem"
|
||||||
certificate = ""
|
# ssl_cert = "/etc/telegraf/cert.pem"
|
||||||
# Client key
|
# ssl_key = "/etc/telegraf/key.pem"
|
||||||
key = ""
|
### Use SSL but skip chain & host verification
|
||||||
# Certificate authority file
|
# insecure_skip_verify = false
|
||||||
ca = ""
|
|
||||||
# Verify SSL certificate chain
|
|
||||||
verify_ssl = false
|
|
||||||
`
|
`
|
||||||
|
|
||||||
func createTlsConfiguration(k *Kafka) (t *tls.Config, err error) {
|
|
||||||
if k.Certificate != "" && k.Key != "" && k.CA != "" {
|
|
||||||
cert, err := tls.LoadX509KeyPair(k.Certificate, k.Key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New(fmt.Sprintf("Cout not load Kafka TLS client key/certificate: %s",
|
|
||||||
err))
|
|
||||||
}
|
|
||||||
|
|
||||||
caCert, err := ioutil.ReadFile(k.CA)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New(fmt.Sprintf("Cout not load Kafka TLS CA: %s",
|
|
||||||
err))
|
|
||||||
}
|
|
||||||
|
|
||||||
caCertPool := x509.NewCertPool()
|
|
||||||
caCertPool.AppendCertsFromPEM(caCert)
|
|
||||||
|
|
||||||
t = &tls.Config{
|
|
||||||
Certificates: []tls.Certificate{cert},
|
|
||||||
RootCAs: caCertPool,
|
|
||||||
InsecureSkipVerify: k.VerifySsl,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// will be nil by default if nothing is provided
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *Kafka) Connect() error {
|
func (k *Kafka) Connect() error {
|
||||||
config := sarama.NewConfig()
|
config := sarama.NewConfig()
|
||||||
config.Producer.RequiredAcks = sarama.WaitForAll // Wait for all in-sync replicas to ack the message
|
// Wait for all in-sync replicas to ack the message
|
||||||
config.Producer.Retry.Max = 10 // Retry up to 10 times to produce the message
|
config.Producer.RequiredAcks = sarama.WaitForAll
|
||||||
tlsConfig, err := createTlsConfiguration(k)
|
// Retry up to 10 times to produce the message
|
||||||
|
config.Producer.Retry.Max = 10
|
||||||
|
|
||||||
|
// Legacy support ssl config
|
||||||
|
if k.Certificate != "" {
|
||||||
|
k.SSLCert = k.Certificate
|
||||||
|
k.SSLCA = k.CA
|
||||||
|
k.SSLKey = k.Key
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConfig, err := internal.GetTLSConfig(
|
||||||
|
k.SSLCert, k.SSLKey, k.SSLCA, k.InsecureSkipVerify)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,16 +28,16 @@ type KinesisOutput struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
# Amazon REGION of kinesis endpoint.
|
### Amazon REGION of kinesis endpoint.
|
||||||
region = "ap-southeast-2"
|
region = "ap-southeast-2"
|
||||||
# Kinesis StreamName must exist prior to starting telegraf.
|
### Kinesis StreamName must exist prior to starting telegraf.
|
||||||
streamname = "StreamName"
|
streamname = "StreamName"
|
||||||
# PartitionKey as used for sharding data.
|
### PartitionKey as used for sharding data.
|
||||||
partitionkey = "PartitionKey"
|
partitionkey = "PartitionKey"
|
||||||
# format of the Data payload in the kinesis PutRecord, supported
|
### format of the Data payload in the kinesis PutRecord, supported
|
||||||
# String and Custom.
|
### String and Custom.
|
||||||
format = "string"
|
format = "string"
|
||||||
# debug will show upstream aws messages.
|
### debug will show upstream aws messages.
|
||||||
debug = false
|
debug = false
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
|
@ -23,20 +23,20 @@ type Librato struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
# Librator API Docs
|
### Librator API Docs
|
||||||
# http://dev.librato.com/v1/metrics-authentication
|
### http://dev.librato.com/v1/metrics-authentication
|
||||||
|
|
||||||
# Librato API user
|
### Librato API user
|
||||||
api_user = "telegraf@influxdb.com" # required.
|
api_user = "telegraf@influxdb.com" # required.
|
||||||
|
|
||||||
# Librato API token
|
### Librato API token
|
||||||
api_token = "my-secret-token" # required.
|
api_token = "my-secret-token" # required.
|
||||||
|
|
||||||
# Tag Field to populate source attribute (optional)
|
### Tag Field to populate source attribute (optional)
|
||||||
# This is typically the _hostname_ from which the metric was obtained.
|
### This is typically the _hostname_ from which the metric was obtained.
|
||||||
source_tag = "hostname"
|
source_tag = "hostname"
|
||||||
|
|
||||||
# Connection timeout.
|
### Connection timeout.
|
||||||
# timeout = "5s"
|
# timeout = "5s"
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
package mqtt
|
package mqtt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -15,7 +11,6 @@ import (
|
||||||
"github.com/influxdata/telegraf/plugins/outputs"
|
"github.com/influxdata/telegraf/plugins/outputs"
|
||||||
)
|
)
|
||||||
|
|
||||||
const MaxClientIdLen = 8
|
|
||||||
const MaxRetryCount = 3
|
const MaxRetryCount = 3
|
||||||
const ClientIdPrefix = "telegraf"
|
const ClientIdPrefix = "telegraf"
|
||||||
|
|
||||||
|
@ -27,22 +22,39 @@ type MQTT struct {
|
||||||
Timeout internal.Duration
|
Timeout internal.Duration
|
||||||
TopicPrefix string
|
TopicPrefix string
|
||||||
|
|
||||||
Client *paho.Client
|
// Path to CA file
|
||||||
Opts *paho.ClientOptions
|
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 *paho.Client
|
||||||
|
opts *paho.ClientOptions
|
||||||
|
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
servers = ["localhost:1883"] # required.
|
servers = ["localhost:1883"] # required.
|
||||||
|
|
||||||
# MQTT outputs send metrics to this topic format
|
### MQTT outputs send metrics to this topic format
|
||||||
# "<topic_prefix>/host/<hostname>/<pluginname>/"
|
### "<topic_prefix>/<hostname>/<pluginname>/"
|
||||||
# ex: prefix/host/web01.example.com/mem/available
|
### ex: prefix/host/web01.example.com/mem
|
||||||
# topic_prefix = "prefix"
|
topic_prefix = "telegraf"
|
||||||
|
|
||||||
# username and password to connect MQTT server.
|
### username and password to connect MQTT server.
|
||||||
# username = "telegraf"
|
# username = "telegraf"
|
||||||
# password = "metricsmetricsmetricsmetrics"
|
# password = "metricsmetricsmetricsmetrics"
|
||||||
|
|
||||||
|
### Optional SSL Config
|
||||||
|
# ssl_ca = "/etc/telegraf/ca.pem"
|
||||||
|
# ssl_cert = "/etc/telegraf/cert.pem"
|
||||||
|
# ssl_key = "/etc/telegraf/key.pem"
|
||||||
|
### Use SSL but skip chain & host verification
|
||||||
|
# insecure_skip_verify = false
|
||||||
`
|
`
|
||||||
|
|
||||||
func (m *MQTT) Connect() error {
|
func (m *MQTT) Connect() error {
|
||||||
|
@ -50,13 +62,13 @@ func (m *MQTT) Connect() error {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
m.Opts, err = m.CreateOpts()
|
m.opts, err = m.createOpts()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.Client = paho.NewClient(m.Opts)
|
m.client = paho.NewClient(m.opts)
|
||||||
if token := m.Client.Connect(); token.Wait() && token.Error() != nil {
|
if token := m.client.Connect(); token.Wait() && token.Error() != nil {
|
||||||
return token.Error()
|
return token.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,8 +76,8 @@ func (m *MQTT) Connect() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MQTT) Close() error {
|
func (m *MQTT) Close() error {
|
||||||
if m.Client.IsConnected() {
|
if m.client.IsConnected() {
|
||||||
m.Client.Disconnect(20)
|
m.client.Disconnect(20)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -94,12 +106,11 @@ func (m *MQTT) Write(metrics []telegraf.Metric) error {
|
||||||
if m.TopicPrefix != "" {
|
if m.TopicPrefix != "" {
|
||||||
t = append(t, m.TopicPrefix)
|
t = append(t, m.TopicPrefix)
|
||||||
}
|
}
|
||||||
tm := strings.Split(p.Name(), "_")
|
if hostname != "" {
|
||||||
if len(tm) < 2 {
|
t = append(t, hostname)
|
||||||
tm = []string{p.Name(), "stat"}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t = append(t, "host", hostname, tm[0], tm[1])
|
t = append(t, p.Name())
|
||||||
topic := strings.Join(t, "/")
|
topic := strings.Join(t, "/")
|
||||||
|
|
||||||
value := p.String()
|
value := p.String()
|
||||||
|
@ -113,7 +124,7 @@ func (m *MQTT) Write(metrics []telegraf.Metric) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MQTT) publish(topic, body string) error {
|
func (m *MQTT) publish(topic, body string) error {
|
||||||
token := m.Client.Publish(topic, 0, false, body)
|
token := m.client.Publish(topic, 0, false, body)
|
||||||
token.Wait()
|
token.Wait()
|
||||||
if token.Error() != nil {
|
if token.Error() != nil {
|
||||||
return token.Error()
|
return token.Error()
|
||||||
|
@ -121,25 +132,22 @@ func (m *MQTT) publish(topic, body string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MQTT) CreateOpts() (*paho.ClientOptions, error) {
|
func (m *MQTT) createOpts() (*paho.ClientOptions, error) {
|
||||||
opts := paho.NewClientOptions()
|
opts := paho.NewClientOptions()
|
||||||
|
|
||||||
clientId := getRandomClientId()
|
opts.SetClientID("Telegraf-Output-" + internal.RandomString(5))
|
||||||
opts.SetClientID(clientId)
|
|
||||||
|
|
||||||
TLSConfig := &tls.Config{InsecureSkipVerify: false}
|
tlsCfg, err := internal.GetTLSConfig(
|
||||||
ca := "" // TODO
|
m.SSLCert, m.SSLKey, m.SSLCA, m.InsecureSkipVerify)
|
||||||
scheme := "tcp"
|
|
||||||
if ca != "" {
|
|
||||||
scheme = "ssl"
|
|
||||||
certPool, err := getCertPool(ca)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
TLSConfig.RootCAs = certPool
|
|
||||||
|
scheme := "tcp"
|
||||||
|
if tlsCfg != nil {
|
||||||
|
scheme = "ssl"
|
||||||
|
opts.SetTLSConfig(tlsCfg)
|
||||||
}
|
}
|
||||||
TLSConfig.InsecureSkipVerify = true // TODO
|
|
||||||
opts.SetTLSConfig(TLSConfig)
|
|
||||||
|
|
||||||
user := m.Username
|
user := m.Username
|
||||||
if user == "" {
|
if user == "" {
|
||||||
|
@ -162,27 +170,6 @@ func (m *MQTT) CreateOpts() (*paho.ClientOptions, error) {
|
||||||
return opts, nil
|
return opts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRandomClientId() string {
|
|
||||||
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
|
||||||
var bytes = make([]byte, MaxClientIdLen)
|
|
||||||
rand.Read(bytes)
|
|
||||||
for i, b := range bytes {
|
|
||||||
bytes[i] = alphanum[b%byte(len(alphanum))]
|
|
||||||
}
|
|
||||||
return ClientIdPrefix + "-" + string(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCertPool(pemPath string) (*x509.CertPool, error) {
|
|
||||||
certs := x509.NewCertPool()
|
|
||||||
|
|
||||||
pemData, err := ioutil.ReadFile(pemPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
certs.AppendCertsFromPEM(pemData)
|
|
||||||
return certs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
outputs.Add("mqtt", func() telegraf.Output {
|
outputs.Add("mqtt", func() telegraf.Output {
|
||||||
return &MQTT{}
|
return &MQTT{}
|
||||||
|
|
|
@ -14,9 +14,9 @@ type NSQ struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
# Location of nsqd instance listening on TCP
|
### Location of nsqd instance listening on TCP
|
||||||
server = "localhost:4150"
|
server = "localhost:4150"
|
||||||
# NSQ topic for producer messages
|
### NSQ topic for producer messages
|
||||||
topic = "telegraf"
|
topic = "telegraf"
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
|
@ -22,17 +22,17 @@ type OpenTSDB struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
# prefix for metrics keys
|
### prefix for metrics keys
|
||||||
prefix = "my.specific.prefix."
|
prefix = "my.specific.prefix."
|
||||||
|
|
||||||
## Telnet Mode ##
|
## Telnet Mode ##
|
||||||
# DNS name of the OpenTSDB server in telnet mode
|
### DNS name of the OpenTSDB server in telnet mode
|
||||||
host = "opentsdb.example.com"
|
host = "opentsdb.example.com"
|
||||||
|
|
||||||
# Port of the OpenTSDB server in telnet mode
|
### Port of the OpenTSDB server in telnet mode
|
||||||
port = 4242
|
port = 4242
|
||||||
|
|
||||||
# Debug true - Prints OpenTSDB communication
|
### Debug true - Prints OpenTSDB communication
|
||||||
debug = false
|
debug = false
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ type PrometheusClient struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
# Address to listen on
|
### Address to listen on
|
||||||
# listen = ":9126"
|
# listen = ":9126"
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,8 @@ func TestPrometheusWritePointEmptyTag(t *testing.T) {
|
||||||
t.Skip("Skipping integration test in short mode")
|
t.Skip("Skipping integration test in short mode")
|
||||||
}
|
}
|
||||||
pTesting = &PrometheusClient{Listen: "localhost:9127"}
|
pTesting = &PrometheusClient{Listen: "localhost:9127"}
|
||||||
pTesting.Start()
|
err := pTesting.Start()
|
||||||
|
require.NoError(t, err)
|
||||||
defer pTesting.Stop()
|
defer pTesting.Stop()
|
||||||
|
|
||||||
p := &prometheus.Prometheus{
|
p := &prometheus.Prometheus{
|
||||||
|
|
|
@ -18,9 +18,9 @@ type Riemann struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
# URL of server
|
### URL of server
|
||||||
url = "localhost:5555"
|
url = "localhost:5555"
|
||||||
# transport protocol to use either tcp or udp
|
### transport protocol to use either tcp or udp
|
||||||
transport = "tcp"
|
transport = "tcp"
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue