Skip to content

Commit

Permalink
Merge pull request #44 from oliver006/oh_ssl_connections
Browse files Browse the repository at this point in the history
Support ssl connections
  • Loading branch information
oliver006 authored Nov 24, 2016
2 parents c81aa3e + 8a81389 commit 556b39d
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 76 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,17 @@ and adjust the host name accordingly.

Name | Description
-------------------|------------
debug | Verbose debug output
log-format | Log format, valid options are `txt` (default) and `json`.
check-keys | Comma separated list of keys to export value and length/size, eg: `db3=user_count` will export key `user_count` from db `3`. db defaults to `0` if omitted.
redis.addr | Address of one or more redis nodes, comma separated, defaults to `localhost:6379`.
redis.addr | Address of one or more redis nodes, comma separated, defaults to `redis://localhost:6379`.
redis.password | Password to use when authenticating to Redis
namespace | Namespace for the metrics, defaults to `redis`.
web.listen-address | Address to listen on for web interface and telemetry, defaults to `0.0.0.0:9121`.
web.telemetry-path | Path under which to expose metrics, defaults to `metrics`.

Redis node addresses can be tcp addresses like `localhost:6379` or unix socket addresses like `unix:///tmp/redis.sock`
Redis node addresses can be tcp addresses like `redis://localhost:6379`, `redis.example.com:6379` or unix socket addresses like `unix:///tmp/redis.sock`. <br>
SSL is supported by using the `rediss://` schema, for example: `rediss://azure-ssl-enabled-host.redis.cache.windows.net:6380` (note that the port is required when connecting to a non-standard 6379 port, e.g. with Azure Redis instances).

These settings take precedence over any configurations provided by [environment variables](#environment-variables).

Expand Down
64 changes: 32 additions & 32 deletions exporter/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package exporter

import (
"fmt"
"log"
"net/url"
"strconv"
"strings"
"sync"
"time"

log "github.com/Sirupsen/logrus"
"github.com/garyburd/redigo/redis"
"github.com/prometheus/client_golang/prometheus"
)
Expand All @@ -23,7 +23,7 @@ type dbKeyPair struct {
db, key string
}

// Exporter implementes the prometheus.Exporter interface, and exports Redis metrics.
// Exporter implements the prometheus.Exporter interface, and exports Redis metrics.
type Exporter struct {
redis RedisHost
namespace string
Expand Down Expand Up @@ -132,16 +132,10 @@ func (e *Exporter) initGauges() {

// NewRedisExporter returns a new exporter of Redis metrics.
// note to self: next time we add an argument, instead add a RedisExporter struct
func NewRedisExporter(redis RedisHost, namespace, checkKeys string) (*Exporter, error) {
for _, addr := range redis.Addrs {
parts := strings.Split(addr, ":")
if len(parts) != 2 {
return nil, fmt.Errorf("Invalid parameter --redis.addr: %s", addr)
}
}
func NewRedisExporter(host RedisHost, namespace, checkKeys string) (*Exporter, error) {

e := Exporter{
redis: redis,
redis: host,
namespace: namespace,
keyValues: prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: namespace,
Expand Down Expand Up @@ -169,8 +163,8 @@ func NewRedisExporter(redis RedisHost, namespace, checkKeys string) (*Exporter,
Help: "The last scrape error status.",
}),
}
var err error
for _, k := range strings.Split(checkKeys, ",") {
var err error
db := "0"
key := ""
frags := strings.Split(k, "=")
Expand All @@ -185,11 +179,14 @@ func NewRedisExporter(redis RedisHost, namespace, checkKeys string) (*Exporter,
err = fmt.Errorf("")
}
if err != nil {
log.Printf("Couldn't parse db/key string: %s", k)
log.Debugf("Couldn't parse db/key string: %s", k)
continue
}
e.keys = append(e.keys, dbKeyPair{db, key})
if key != "" {
e.keys = append(e.keys, dbKeyPair{db, key})
}
}

e.initGauges()
return &e, nil
}
Expand Down Expand Up @@ -291,7 +288,7 @@ func (e *Exporter) extractInfoMetrics(info, addr string, scrapes chan<- scrapeRe
cmdstats := false
lines := strings.Split(info, "\r\n")
for _, line := range lines {

log.Debugf("info: %s", line)
if len(line) > 0 && line[0] == '#' {
if strings.Contains(line, "Commandstats") {
cmdstats = true
Expand All @@ -315,7 +312,6 @@ func (e *Exporter) extractInfoMetrics(info, addr string, scrapes chan<- scrapeRe
cmdstat_set:calls=61,usec=3139,usec_per_call=51.46
cmdstat_setex:calls=75,usec=1260,usec_per_call=16.80
*/

frags := strings.Split(split[0], "_")
if len(frags) != 2 {
continue
Expand Down Expand Up @@ -375,7 +371,7 @@ func (e *Exporter) extractInfoMetrics(info, addr string, scrapes chan<- scrapeRe

}
if err != nil {
log.Printf("couldn't parse %s, err: %s", split[1], err)
log.Debugf("couldn't parse %s, err: %s", split[1], err)
continue
}

Expand All @@ -393,7 +389,7 @@ func extractConfigMetrics(config []string, addr string, scrapes chan<- scrapeRes
for pos := 0; pos < len(config)/2; pos++ {
val, err := strconv.ParseFloat(config[pos*2+1], 64)
if err != nil {
log.Printf("couldn't parse %s, err: %s", config[pos*2+1], err)
log.Debugf("couldn't parse %s, err: %s", config[pos*2+1], err)
continue
}
scrapes <- scrapeResult{Name: fmt.Sprintf("config_%s", config[pos*2]), Addr: addr, Value: val}
Expand All @@ -409,29 +405,33 @@ func (e *Exporter) scrape(scrapes chan<- scrapeResult) {

errorCount := 0
for idx, addr := range e.redis.Addrs {
split := strings.Split(addr, "://")
proto := "tcp"
realAddr := addr
if len(split) == 2 {
proto = strings.TrimSpace(split[0])
realAddr = strings.TrimSpace(split[1])
var c redis.Conn
var err error

var options []redis.DialOption
if len(e.redis.Passwords) > idx && e.redis.Passwords[idx] != "" {
options = append(options, redis.DialPassword(e.redis.Passwords[idx]))
}

log.Debugf("Trying DialURL(): %s", addr)
if c, err = redis.DialURL(addr, options...); err != nil {
frags := strings.Split(addr, "://")
if len(frags) == 2 {
log.Debugf("Trying: Dial(): %s %s", frags[0], frags[1])
c, err = redis.Dial(frags[0], frags[1], options...)
} else {
log.Debugf("Trying: Dial(): tcp %s", addr)
c, err = redis.Dial("tcp", addr, options...)
}
}

c, err := redis.Dial(proto, realAddr)
if err != nil {
log.Printf("redis err: %s", err)
errorCount++
continue
}
defer c.Close()

if len(e.redis.Passwords) > idx && e.redis.Passwords[idx] != "" {
if _, err := c.Do("AUTH", e.redis.Passwords[idx]); err != nil {
log.Printf("redis err: %s", err)
errorCount++
continue
}
}
log.Debugf("connected to: %s", addr)

info, err := redis.String(c.Do("INFO", "ALL"))
if err == nil {
Expand Down
66 changes: 31 additions & 35 deletions exporter/redis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ package exporter
*/

import (
"flag"
"fmt"
"io/ioutil"
"log"
Expand All @@ -20,16 +19,19 @@ import (
"time"

"bytes"
"flag"
"github.com/garyburd/redigo/redis"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
)

var (
keys = []string{}
keysExpiring = []string{}
ts = int32(time.Now().Unix())
r = RedisHost{}
redisAddr = flag.String("redis.addr", "localhost:6379", "Address of the test instance, without `redis://`")

keys = []string{}
keysExpiring = []string{}
ts = int32(time.Now().Unix())
defaultRedisHost = RedisHost{}

dbNumStr = "11"
dbNumStrFull = fmt.Sprintf("db%s", dbNumStr)
Expand All @@ -41,7 +43,7 @@ const (

func setupDBKeys(t *testing.T) error {

c, err := redis.Dial("tcp", r.Addrs[0])
c, err := redis.DialURL(defaultRedisHost.Addrs[0])
if err != nil {
t.Errorf("couldn't setup redis, err: %s ", err)
return err
Expand Down Expand Up @@ -81,7 +83,7 @@ func setupDBKeys(t *testing.T) error {

func deleteKeysFromDB(t *testing.T) error {

c, err := redis.Dial("tcp", r.Addrs[0])
c, err := redis.DialURL(defaultRedisHost.Addrs[0])
if err != nil {
t.Errorf("couldn't setup redis, err: %s ", err)
return err
Expand All @@ -107,33 +109,28 @@ func deleteKeysFromDB(t *testing.T) error {
return nil
}

func TestNewRedisExporter(t *testing.T) {
cases := []struct {
addrs []string
ok bool
}{
{addrs: []string{""}, ok: false},
{addrs: []string{"localhost"}, ok: false},
{addrs: []string{"localhost:1234"}, ok: true},
{addrs: []string{"localhost:1234", "another.one:6379"}, ok: true},
{addrs: []string{"another.one:6379", "redis.somewhere.com"}, ok: false},
}

for _, test := range cases {
rh := RedisHost{Addrs: test.addrs}
_, err := NewRedisExporter(rh, "redis", "")
if err == nil && !test.ok {
t.Error("expected error but got nil")
func TestHostVariations(t *testing.T) {
for _, prefix := range []string{"", "redis://", "tcp://"} {
addr := prefix + *redisAddr
host := RedisHost{Addrs: []string{addr}}
e, _ := NewRedisExporter(host, "test", "")

scrapes := make(chan scrapeResult, 10000)
e.scrape(scrapes)
found := 0
for range scrapes {
found++
}
if err != nil && test.ok {
t.Errorf("expected no error but got %s", err)

if found == 0 {
t.Errorf("didn't find any scrapes for host: %s", addr)
}
}
}

func TestCountingKeys(t *testing.T) {

e, _ := NewRedisExporter(r, "test", "")
e, _ := NewRedisExporter(defaultRedisHost, "test", "")

scrapes := make(chan scrapeResult, 10000)
e.scrape(scrapes)
Expand Down Expand Up @@ -186,7 +183,7 @@ func TestCountingKeys(t *testing.T) {

func TestExporterMetrics(t *testing.T) {

e, _ := NewRedisExporter(r, "test", "")
e, _ := NewRedisExporter(defaultRedisHost, "test", "")

setupDBKeys(t)
defer deleteKeysFromDB(t)
Expand Down Expand Up @@ -217,7 +214,7 @@ func TestExporterMetrics(t *testing.T) {

func TestExporterValues(t *testing.T) {

e, _ := NewRedisExporter(r, "test", "")
e, _ := NewRedisExporter(defaultRedisHost, "test", "")

setupDBKeys(t)
defer deleteKeysFromDB(t)
Expand Down Expand Up @@ -278,7 +275,7 @@ func TestKeyspaceStringParser(t *testing.T) {

func TestKeyValuesAndSizes(t *testing.T) {

e, _ := NewRedisExporter(r, "test", dbNumStrFull+"="+url.QueryEscape(keys[0]))
e, _ := NewRedisExporter(defaultRedisHost, "test", dbNumStrFull+"="+url.QueryEscape(keys[0]))

setupDBKeys(t)
defer deleteKeysFromDB(t)
Expand Down Expand Up @@ -313,7 +310,7 @@ func TestKeyValuesAndSizes(t *testing.T) {

func TestCommandStats(t *testing.T) {

e, _ := NewRedisExporter(r, "test", dbNumStrFull+"="+url.QueryEscape(keys[0]))
e, _ := NewRedisExporter(defaultRedisHost, "test", dbNumStrFull+"="+url.QueryEscape(keys[0]))

setupDBKeys(t)
defer deleteKeysFromDB(t)
Expand Down Expand Up @@ -348,7 +345,7 @@ func TestCommandStats(t *testing.T) {

func TestHTTPEndpoint(t *testing.T) {

e, _ := NewRedisExporter(r, "test", dbNumStrFull+"="+url.QueryEscape(keys[0]))
e, _ := NewRedisExporter(defaultRedisHost, "test", dbNumStrFull+"="+url.QueryEscape(keys[0]))

setupDBKeys(t)
defer deleteKeysFromDB(t)
Expand Down Expand Up @@ -445,13 +442,12 @@ func init() {
keysExpiring = append(keysExpiring, key)
}

redisAddr := flag.String("redis.addr", "localhost:6379", "Address of one or more redis nodes, separated by separator")

flag.Parse()
addrs := strings.Split(*redisAddr, ",")
if len(addrs) == 0 || len(addrs[0]) == 0 {
log.Fatal("Invalid parameter --redis.addr")
}
log.Printf("Using redis addrs: %#v", addrs)
r = RedisHost{Addrs: addrs}

defaultRedisHost = RedisHost{Addrs: []string{"redis://" + *redisAddr}}
}
Loading

0 comments on commit 556b39d

Please sign in to comment.