Skip to content
Snippets Groups Projects
Unverified Commit ed524c82 authored by Oliver's avatar Oliver Committed by GitHub
Browse files

Merge pull request #119 from oliver006/oh_cluster_tests

Add cluster tests
parents 09e179cd 7d230f3f
No related branches found
No related tags found
No related merge requests found
Loading
@@ -6,10 +6,11 @@ machine:
Loading
@@ -6,10 +6,11 @@ machine:
- /usr/bin/redis-server --port 6380: - /usr/bin/redis-server --port 6380:
background: true background: true
environment: environment:
SRC_LOCATION: "/home/ubuntu/.go_workspace/src/github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME" SRC_LOCATION: "/home/ubuntu/.go_workspace/src/github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME"
COVERAGE_PROFILE: "/home/ubuntu/coverage.out" COVERAGE_PROFILE: "/home/ubuntu/coverage.out"
GO_LDFLAGS: '-extldflags "-static" -X main.VERSION=$CIRCLE_TAG -X main.COMMIT_SHA1=$CIRCLE_SHA1 -X main.BUILD_DATE=$(date +%F-%T)' GO_LDFLAGS: '-extldflags "-static" -X main.VERSION=$CIRCLE_TAG -X main.COMMIT_SHA1=$CIRCLE_SHA1 -X main.BUILD_DATE=$(date +%F-%T)'
MY_GO_VERSION: "1.9.2" MY_GO_VERSION: "1.9.4"
REDIS_TEST_VERSION: "3.2.11"
   
dependencies: dependencies:
pre: pre:
Loading
@@ -17,16 +18,20 @@ dependencies:
Loading
@@ -17,16 +18,20 @@ dependencies:
- rm -rf /home/ubuntu/.go_project - rm -rf /home/ubuntu/.go_project
- sudo service redis-server stop - sudo service redis-server stop
- > - >
cd ~ && if [ ! -d "redis-3.2.10" ]; then cd ~ && if [ ! -d "redis-$REDIS_TEST_VERSION" ]; then
wget http://download.redis.io/releases/redis-3.2.10.tar.gz wget http://download.redis.io/releases/redis-$REDIS_TEST_VERSION.tar.gz
tar xzf redis-3.2.10.tar.gz tar xzf redis-$REDIS_TEST_VERSION.tar.gz
cd redis-3.2.10 && make; cd redis-$REDIS_TEST_VERSION && make;
fi fi
- cd ~/redis-3.2.10 && sudo make install - cd ~/redis-$REDIS_TEST_VERSION && sudo make install
- sudo sed -i 's/bin/local\/bin/g' /etc/init/redis-server.conf - sudo sed -i 's/bin/local\/bin/g' /etc/init/redis-server.conf
- sudo service redis-server start - sudo service redis-server start
#
# the next line will bring up a cluster of redis instances with slaves
# for addtl tests
- docker run -p 7000-7006:7000-7006 --name redis_cluster_test -d grokzen/redis-cluster
cache_directories: cache_directories:
- ~/redis-3.2.10 - ~/redis-$REDIS_TEST_VERSION
override: override:
- mkdir -p "/home/ubuntu/.go_workspace/src/github.com/$CIRCLE_PROJECT_USERNAME" - mkdir -p "/home/ubuntu/.go_workspace/src/github.com/$CIRCLE_PROJECT_USERNAME"
- ln -s $HOME/$CIRCLE_PROJECT_REPONAME $SRC_LOCATION - ln -s $HOME/$CIRCLE_PROJECT_REPONAME $SRC_LOCATION
Loading
@@ -41,7 +46,7 @@ test:
Loading
@@ -41,7 +46,7 @@ test:
- go get github.com/mattn/goveralls - go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover - go get golang.org/x/tools/cmd/cover
override: override:
- cd $SRC_LOCATION && go test -v -covermode=atomic -cover -race -coverprofile=$COVERAGE_PROFILE ./exporter/... - cd $SRC_LOCATION && TEST_REDIS_CLUSTER_MASTER_URI=localhost:7000 TEST_REDIS_CLUSTER_SLAVE_URI=localhost:7005 go test -v -covermode=atomic -cover -race -coverprofile=$COVERAGE_PROFILE ./exporter/...
post: post:
- if [ -n "$COVERALLS_TOKEN" ]; then /home/ubuntu/.go_workspace/bin/goveralls -coverprofile=$COVERAGE_PROFILE -service=circle-ci -repotoken=$COVERALLS_TOKEN ; fi - if [ -n "$COVERALLS_TOKEN" ]; then /home/ubuntu/.go_workspace/bin/goveralls -coverprofile=$COVERAGE_PROFILE -service=circle-ci -repotoken=$COVERALLS_TOKEN ; fi
   
Loading
Loading
redis://localhost:6379
redis://localhost:7000,password,alias
redis://localhost:7000,second-pwd
\ No newline at end of file
package exporter
import (
"encoding/csv"
"os"
"strings"
"github.com/cloudfoundry-community/go-cfenv"
log "github.com/sirupsen/logrus"
)
// loadRedisArgs loads the configuration for which redis hosts to monitor from either
// the environment or as passed from program arguments. Returns the list of host addrs,
// passwords, and their aliases.
func LoadRedisArgs(addr, password, alias, separator string) ([]string, []string, []string) {
if addr == "" {
addr = "redis://localhost:6379"
}
addrs := strings.Split(addr, separator)
passwords := strings.Split(password, separator)
for len(passwords) < len(addrs) {
passwords = append(passwords, passwords[0])
}
aliases := strings.Split(alias, separator)
for len(aliases) < len(addrs) {
aliases = append(aliases, aliases[0])
}
return addrs, passwords, aliases
}
// loadRedisFile opens the specified file and loads the configuration for which redis
// hosts to monitor. Returns the list of hosts addrs, passwords, and their aliases.
func LoadRedisFile(fileName string) ([]string, []string, []string, error) {
var addrs []string
var passwords []string
var aliases []string
file, err := os.Open(fileName)
if err != nil {
return nil, nil, nil, err
}
r := csv.NewReader(file)
r.FieldsPerRecord = -1
records, err := r.ReadAll()
if err != nil {
return nil, nil, nil, err
}
file.Close()
// For each line, test if it contains an optional password and alias and provide them,
// else give them empty strings
for _, record := range records {
length := len(record)
switch length {
case 3:
addrs = append(addrs, record[0])
passwords = append(passwords, record[1])
aliases = append(aliases, record[2])
case 2:
addrs = append(addrs, record[0])
passwords = append(passwords, record[1])
aliases = append(aliases, "")
case 1:
addrs = append(addrs, record[0])
passwords = append(passwords, "")
aliases = append(aliases, "")
}
}
return addrs, passwords, aliases, nil
}
func GetCloudFoundryRedisBindings() (addrs, passwords, aliases []string) {
if !cfenv.IsRunningOnCF() {
return
}
appEnv, err := cfenv.Current()
if err != nil {
log.Warnln("Unable to get current CF environment", err)
return
}
redisServices, err := appEnv.Services.WithTag("redis")
if err != nil {
log.Warnln("Error while getting redis services", err)
return
}
for _, redisService := range redisServices {
credentials := redisService.Credentials
addr := credentials["hostname"].(string) + ":" + credentials["port"].(string)
password := credentials["password"].(string)
alias := redisService.Name
addrs = append(addrs, addr)
passwords = append(passwords, password)
aliases = append(aliases, alias)
}
return
}
package exporter
import (
"log"
"testing"
)
func cmpStringArrays(a1, a2 []string) bool {
if len(a1) != len(a2) {
return false
}
for n := range a1 {
if a1[n] != a2[n] {
return false
}
}
return true
}
func TestLoadRedisArgs(t *testing.T) {
log.Println("TestLoadRedisArgs()")
tests := []struct {
addr, pwd, alias, sep string
wantAddr, wantPwds, wantAliases []string
}{
{
addr: "",
sep: ",",
wantAddr: []string{"redis://localhost:6379"},
wantPwds: []string{""},
wantAliases: []string{""},
},
{
addr: "redis://localhost:6379",
sep: ",",
wantAddr: []string{"redis://localhost:6379"},
wantPwds: []string{""},
wantAliases: []string{""},
},
{
addr: "redis://localhost:6379,redis://localhost:7000",
sep: ",",
wantAddr: []string{"redis://localhost:6379", "redis://localhost:7000"},
wantPwds: []string{"", ""},
wantAliases: []string{"", ""},
},
{
addr: "redis://localhost:6379,redis://localhost:7000,redis://localhost:7001",
sep: ",",
wantAddr: []string{"redis://localhost:6379", "redis://localhost:7000", "redis://localhost:7001"},
wantPwds: []string{"", "", ""},
wantAliases: []string{"", "", ""},
},
{
alias: "host-1",
sep: ",",
wantAddr: []string{"redis://localhost:6379"},
wantPwds: []string{""},
wantAliases: []string{"host-1"},
},
}
for _, test := range tests {
sep := test.sep
addrs, pwds, aliases := LoadRedisArgs(test.addr, test.pwd, test.alias, sep)
if !cmpStringArrays(addrs, test.wantAddr) {
t.Errorf("addrs not matching wantAliases, got: %v want: %v", addrs, test.wantAddr)
}
if !cmpStringArrays(pwds, test.wantPwds) {
t.Errorf("pwds not matching wantAliases, got: %v want: %v", pwds, test.wantPwds)
}
if !cmpStringArrays(aliases, test.wantAliases) {
t.Errorf("aliases not matching wantAliases, got: %v want: %v", aliases, test.wantAliases)
}
}
}
func TestLoadRedisFile(t *testing.T) {
if _, _, _, err := LoadRedisFile("doesnt-exist.txt"); err == nil {
t.Errorf("should have failed opening non existing file")
return
}
addrs, pwds, aliases, err := LoadRedisFile("../contrib/sample_redis_hosts_file.txt")
if err != nil {
t.Errorf("LoadRedisFile() failed, err: %s", err)
return
}
log.Printf("aliases: %v \n", aliases)
if !cmpStringArrays(addrs, []string{"redis://localhost:6379", "redis://localhost:7000", "redis://localhost:7000"}) {
t.Errorf("addrs not matching want")
}
if !cmpStringArrays(pwds, []string{"", "password", "second-pwd"}) {
t.Errorf("pwds not matching want")
}
if !cmpStringArrays(aliases, []string{"", "alias", ""}) {
t.Errorf("aliases not matching want")
}
}
func TestGetCloudFoundryRedisBindings(t *testing.T) {
GetCloudFoundryRedisBindings()
}
Loading
@@ -570,6 +570,16 @@ func (e *Exporter) extractInfoMetrics(info, addr string, alias string, scrapes c
Loading
@@ -570,6 +570,16 @@ func (e *Exporter) extractInfoMetrics(info, addr string, alias string, scrapes c
return nil return nil
} }
   
func doRedisCmd(c redis.Conn, cmd string, args ...interface{}) (reply interface{}, err error) {
log.Debugf("c.Do() - running command: %s %s", cmd, args)
defer log.Debugf("c.Do() - done")
res, err := c.Do(cmd, args...)
if err != nil {
log.Debugf("c.Do() - err: %s", err)
}
return res, err
}
func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx int) error { func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx int) error {
options := []redis.DialOption{ options := []redis.DialOption{
redis.DialConnectTimeout(5 * time.Second), redis.DialConnectTimeout(5 * time.Second),
Loading
@@ -596,7 +606,7 @@ func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx
Loading
@@ -596,7 +606,7 @@ func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx
} }
   
if err != nil { if err != nil {
log.Printf("redis err: %s", err) log.Debugf("aborting for addr: %s - redis err: %s", addr, err)
return err return err
} }
   
Loading
@@ -615,7 +625,7 @@ func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx
Loading
@@ -615,7 +625,7 @@ func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx
log.Debugf("Redis CONFIG err: %s", err) log.Debugf("Redis CONFIG err: %s", err)
} }
   
info, err := redis.String(c.Do("INFO", "ALL")) info, err := redis.String(doRedisCmd(c, "INFO", "ALL"))
if err == nil { if err == nil {
e.extractInfoMetrics(info, addr, e.redis.Aliases[idx], scrapes, dbCount, true) e.extractInfoMetrics(info, addr, e.redis.Aliases[idx], scrapes, dbCount, true)
} else { } else {
Loading
@@ -623,8 +633,8 @@ func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx
Loading
@@ -623,8 +633,8 @@ func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx
return err return err
} }
   
if strings.Index(info, "cluster_enabled:1") != -1 { if strings.Contains(info, "cluster_enabled:1") {
info, err = redis.String(c.Do("CLUSTER", "INFO")) info, err = redis.String(doRedisCmd(c, "CLUSTER", "INFO"))
if err != nil { if err != nil {
log.Errorf("redis err: %s", err) log.Errorf("redis err: %s", err)
} else { } else {
Loading
@@ -632,7 +642,7 @@ func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx
Loading
@@ -632,7 +642,7 @@ func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx
} }
} }
   
if reply, err := c.Do("LATENCY", "LATEST"); err == nil { if reply, err := doRedisCmd(c, "LATENCY", "LATEST"); err == nil {
var eventName string var eventName string
var spikeLast, milliseconds, max int64 var spikeLast, milliseconds, max int64
if tempVal, _ := reply.([]interface{}); len(tempVal) > 0 { if tempVal, _ := reply.([]interface{}); len(tempVal) > 0 {
Loading
@@ -646,13 +656,14 @@ func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx
Loading
@@ -646,13 +656,14 @@ func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx
} }
} }
   
log.Debugf("e.keys: %#v", e.keys)
for _, k := range e.keys { for _, k := range e.keys {
if _, err := c.Do("SELECT", k.db); err != nil { if _, err := doRedisCmd(c, "SELECT", k.db); err != nil {
continue continue
} }
   
obtainedKeys := []string{} obtainedKeys := []string{}
if tempVal, err := redis.Strings(c.Do("KEYS", k.key)); err == nil && tempVal != nil { if tempVal, err := redis.Strings(doRedisCmd(c, "KEYS", k.key)); err == nil && tempVal != nil {
for _, tempKey := range tempVal { for _, tempKey := range tempVal {
log.Debugf("Append result: %s", tempKey) log.Debugf("Append result: %s", tempKey)
obtainedKeys = append(obtainedKeys, tempKey) obtainedKeys = append(obtainedKeys, tempKey)
Loading
@@ -662,7 +673,7 @@ func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx
Loading
@@ -662,7 +673,7 @@ func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx
for _, key := range obtainedKeys { for _, key := range obtainedKeys {
dbLabel := "db" + k.db dbLabel := "db" + k.db
keyLabel := key keyLabel := key
if tempVal, err := c.Do("GET", key); err == nil && tempVal != nil { if tempVal, err := doRedisCmd(c, "GET", key); err == nil && tempVal != nil {
if val, err := strconv.ParseFloat(fmt.Sprintf("%s", tempVal), 64); err == nil { if val, err := strconv.ParseFloat(fmt.Sprintf("%s", tempVal), 64); err == nil {
e.keyValues.WithLabelValues(addr, e.redis.Aliases[idx], dbLabel, keyLabel).Set(val) e.keyValues.WithLabelValues(addr, e.redis.Aliases[idx], dbLabel, keyLabel).Set(val)
} }
Loading
@@ -676,7 +687,7 @@ func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx
Loading
@@ -676,7 +687,7 @@ func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx
"PFCOUNT", "PFCOUNT",
"STRLEN", "STRLEN",
} { } {
if tempVal, err := c.Do(op, key); err == nil && tempVal != nil { if tempVal, err := doRedisCmd(c, op, key); err == nil && tempVal != nil {
e.keySizes.WithLabelValues(addr, e.redis.Aliases[idx], dbLabel, keyLabel).Set(float64(tempVal.(int64))) e.keySizes.WithLabelValues(addr, e.redis.Aliases[idx], dbLabel, keyLabel).Set(float64(tempVal.(int64)))
break break
} }
Loading
@@ -684,18 +695,18 @@ func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx
Loading
@@ -684,18 +695,18 @@ func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx
} }
} }
   
log.Debugf("scrapeRedisHost() done")
return nil return nil
} }
   
func (e *Exporter) scrape(scrapes chan<- scrapeResult) { func (e *Exporter) scrape(scrapes chan<- scrapeResult) {
defer close(scrapes) defer close(scrapes)
now := time.Now().UnixNano() now := time.Now().UnixNano()
e.totalScrapes.Inc() e.totalScrapes.Inc()
   
errorCount := 0 errorCount := 0
for idx, addr := range e.redis.Addrs { for idx, addr := range e.redis.Addrs {
var up float64 = 1 var up float64 = 1
if err := e.scrapeRedisHost(scrapes, addr, idx); err != nil { if err := e.scrapeRedisHost(scrapes, addr, idx); err != nil {
errorCount++ errorCount++
Loading
Loading
Loading
@@ -13,10 +13,10 @@ import (
Loading
@@ -13,10 +13,10 @@ import (
"flag" "flag"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
"os"
"strings" "strings"
"testing" "testing"
"time" "time"
Loading
@@ -25,6 +25,7 @@ import (
Loading
@@ -25,6 +25,7 @@ import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
dto "github.com/prometheus/client_model/go" dto "github.com/prometheus/client_model/go"
log "github.com/sirupsen/logrus"
) )
   
const ( const (
Loading
@@ -100,25 +101,21 @@ func resetLatency(t *testing.T, addr string) error {
Loading
@@ -100,25 +101,21 @@ func resetLatency(t *testing.T, addr string) error {
return nil return nil
} }
   
func getMetrics(t *testing.T) []byte { func downloadUrl(t *testing.T, url string) []byte {
url := TestServerURL + "/metrics" log.Debugf("downloadURL() %s", url)
log.Printf("Getting metrics from: %s", url)
resp, err := http.Get(url) resp, err := http.Get(url)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
return body return body
} }
   
func TestLatencySpike(t *testing.T) { func TestLatencySpike(t *testing.T) {
e, _ := NewRedisExporter(defaultRedisHost, "test", "") e, _ := NewRedisExporter(defaultRedisHost, "test", "")
   
setupLatency(t, defaultRedisHost.Addrs[0]) setupLatency(t, defaultRedisHost.Addrs[0])
Loading
@@ -230,7 +227,6 @@ func deleteKeysFromDB(t *testing.T, addr string) error {
Loading
@@ -230,7 +227,6 @@ func deleteKeysFromDB(t *testing.T, addr string) error {
} }
   
c.Do("DEL", TestSetName) c.Do("DEL", TestSetName)
return nil return nil
} }
   
Loading
@@ -574,13 +570,16 @@ func TestCommandStats(t *testing.T) {
Loading
@@ -574,13 +570,16 @@ func TestCommandStats(t *testing.T) {
} }
   
func TestHTTPEndpoint(t *testing.T) { func TestHTTPEndpoint(t *testing.T) {
ts := httptest.NewServer(promhttp.Handler())
defer ts.Close()
e, _ := NewRedisExporter(defaultRedisHost, "test", dbNumStrFull+"="+url.QueryEscape(keys[0])) e, _ := NewRedisExporter(defaultRedisHost, "test", dbNumStrFull+"="+url.QueryEscape(keys[0]))
   
setupDBKeys(t, defaultRedisHost.Addrs[0]) setupDBKeys(t, defaultRedisHost.Addrs[0])
defer deleteKeysFromDB(t, defaultRedisHost.Addrs[0]) defer deleteKeysFromDB(t, defaultRedisHost.Addrs[0])
prometheus.Register(e) prometheus.Register(e)
   
body := getMetrics(t) body := downloadUrl(t, ts.URL+"/metrics")
   
tests := []string{ tests := []string{
// metrics // metrics
Loading
@@ -603,12 +602,10 @@ func TestHTTPEndpoint(t *testing.T) {
Loading
@@ -603,12 +602,10 @@ func TestHTTPEndpoint(t *testing.T) {
} }
   
func TestNonExistingHost(t *testing.T) { func TestNonExistingHost(t *testing.T) {
rr := RedisHost{Addrs: []string{"unix:///tmp/doesnt.exist"}, Aliases: []string{""}} rr := RedisHost{Addrs: []string{"unix:///tmp/doesnt.exist"}, Aliases: []string{""}}
e, _ := NewRedisExporter(rr, "test", "") e, _ := NewRedisExporter(rr, "test", "")
   
chM := make(chan prometheus.Metric) chM := make(chan prometheus.Metric)
go func() { go func() {
e.Collect(chM) e.Collect(chM)
close(chM) close(chM)
Loading
@@ -757,6 +754,9 @@ func TestSanitizeMetricName(t *testing.T) {
Loading
@@ -757,6 +754,9 @@ func TestSanitizeMetricName(t *testing.T) {
} }
   
func TestKeysReset(t *testing.T) { func TestKeysReset(t *testing.T) {
ts := httptest.NewServer(promhttp.Handler())
defer ts.Close()
e, _ := NewRedisExporter(defaultRedisHost, "test", dbNumStrFull+"="+keys[0]) e, _ := NewRedisExporter(defaultRedisHost, "test", dbNumStrFull+"="+keys[0])
   
setupDBKeys(t, defaultRedisHost.Addrs[0]) setupDBKeys(t, defaultRedisHost.Addrs[0])
Loading
@@ -764,13 +764,13 @@ func TestKeysReset(t *testing.T) {
Loading
@@ -764,13 +764,13 @@ func TestKeysReset(t *testing.T) {
   
prometheus.Register(e) prometheus.Register(e)
   
chM := make(chan prometheus.Metric) chM := make(chan prometheus.Metric, 10000)
go func() { go func() {
e.Collect(chM) e.Collect(chM)
close(chM) close(chM)
}() }()
   
body := getMetrics(t) body := downloadUrl(t, ts.URL+"/metrics")
   
if !bytes.Contains(body, []byte(keys[0])) { if !bytes.Contains(body, []byte(keys[0])) {
t.Errorf("Did not found key %q\n%s", keys[0], body) t.Errorf("Did not found key %q\n%s", keys[0], body)
Loading
@@ -778,14 +778,182 @@ func TestKeysReset(t *testing.T) {
Loading
@@ -778,14 +778,182 @@ func TestKeysReset(t *testing.T) {
   
deleteKeysFromDB(t, defaultRedisHost.Addrs[0]) deleteKeysFromDB(t, defaultRedisHost.Addrs[0])
   
body = getMetrics(t) body = downloadUrl(t, ts.URL+"/metrics")
   
if bytes.Contains(body, []byte(keys[0])) { if bytes.Contains(body, []byte(keys[0])) {
t.Errorf("Metric is present in metrics list %q\n%s", keys[0], body) t.Errorf("Metric is present in metrics list %q\n%s", keys[0], body)
} }
} }
   
func TestClusterMaster(t *testing.T) {
if os.Getenv("TEST_REDIS_CLUSTER_MASTER_URI") == "" {
log.Println("TEST_REDIS_CLUSTER_MASTER_URI not set - skipping")
t.SkipNow()
return
}
ts := httptest.NewServer(promhttp.Handler())
defer ts.Close()
addr := "redis://" + os.Getenv("TEST_REDIS_CLUSTER_MASTER_URI")
host := RedisHost{Addrs: []string{addr}, Aliases: []string{"master"}}
e, _ := NewRedisExporter(host, "test", "")
setupDBKeys(t, defaultRedisHost.Addrs[0])
defer deleteKeysFromDB(t, defaultRedisHost.Addrs[0])
log.Println("wut")
prometheus.Register(e)
chM := make(chan prometheus.Metric, 10000)
go func() {
e.Collect(chM)
close(chM)
}()
body := downloadUrl(t, ts.URL+"/metrics")
if !bytes.Contains(body, []byte("test_instance_info")) {
t.Errorf("Did not found key %q\n%s", keys[0], body)
}
}
func TestPasswordProtectedInstance(t *testing.T) {
ts := httptest.NewServer(promhttp.Handler())
defer ts.Close()
testPwd := "p4$$w0rd"
host := defaultRedisHost
host.Passwords = []string{testPwd}
setupDBKeys(t, host.Addrs[0])
// set password for redis instance
c, err := redis.DialURL(host.Addrs[0])
if err != nil {
t.Errorf("couldn't setup redis, err: %s ", err)
return
}
defer c.Close()
if _, err = c.Do("CONFIG", "SET", "requirepass", testPwd); err != nil {
t.Fatalf("error setting password, err: %s", err)
}
c.Flush()
defer func() {
if _, err = c.Do("auth", testPwd); err != nil {
t.Fatalf("error unsetting password, err: %s", err)
}
if _, err = c.Do("CONFIG", "SET", "requirepass", ""); err != nil {
t.Fatalf("error unsetting password, err: %s", err)
}
deleteKeysFromDB(t, host.Addrs[0])
}()
e, _ := NewRedisExporter(host, "test", "")
prometheus.Register(e)
chM := make(chan prometheus.Metric, 10000)
go func() {
e.Collect(chM)
close(chM)
}()
body := downloadUrl(t, ts.URL+"/metrics")
if !bytes.Contains(body, []byte("test_up")) {
t.Errorf("error")
}
}
func TestPasswordInvalid(t *testing.T) {
ts := httptest.NewServer(promhttp.Handler())
defer ts.Close()
testPwd := "p4$$w0rd"
host := defaultRedisHost
host.Passwords = []string{"wrong_password"}
setupDBKeys(t, host.Addrs[0])
// set password for redis instance
c, err := redis.DialURL(host.Addrs[0])
if err != nil {
t.Errorf("couldn't setup redis, err: %s ", err)
return
}
defer c.Close()
if _, err = c.Do("CONFIG", "SET", "requirepass", testPwd); err != nil {
t.Fatalf("error setting password, err: %s", err)
}
c.Flush()
defer func() {
if _, err = c.Do("auth", testPwd); err != nil {
t.Fatalf("error unsetting password, err: %s", err)
}
if _, err = c.Do("CONFIG", "SET", "requirepass", ""); err != nil {
t.Fatalf("error unsetting password, err: %s", err)
}
deleteKeysFromDB(t, host.Addrs[0])
}()
e, _ := NewRedisExporter(host, "test", "")
prometheus.Register(e)
chM := make(chan prometheus.Metric, 10000)
go func() {
e.Collect(chM)
close(chM)
}()
body := downloadUrl(t, ts.URL+"/metrics")
log.Println(string(body))
if !bytes.Contains(body, []byte("test_exporter_last_scrape_error 1")) {
t.Errorf(`error, expected string "test_exporter_last_scrape_error 1" in body`)
}
}
func TestClusterSlave(t *testing.T) {
if os.Getenv("TEST_REDIS_CLUSTER_SLAVE_URI") == "" {
log.Println("TEST_REDIS_CLUSTER_SLAVE_URI not set - skipping")
t.SkipNow()
return
}
ts := httptest.NewServer(promhttp.Handler())
defer ts.Close()
addr := "redis://" + os.Getenv("TEST_REDIS_CLUSTER_SLAVE_URI")
host := RedisHost{Addrs: []string{addr}, Aliases: []string{"slave"}}
e, _ := NewRedisExporter(host, "test", "")
setupDBKeys(t, defaultRedisHost.Addrs[0])
defer deleteKeysFromDB(t, defaultRedisHost.Addrs[0])
prometheus.Register(e)
chM := make(chan prometheus.Metric, 10000)
go func() {
e.Collect(chM)
close(chM)
}()
body := downloadUrl(t, ts.URL+"/metrics")
if !bytes.Contains(body, []byte("test_instance_info")) {
t.Errorf("Did not found key %q\n%s", keys[0], body)
}
}
func init() { func init() {
ll := strings.ToLower(os.Getenv("LOG_LEVEL"))
if pl, err := log.ParseLevel(ll); err == nil {
log.Printf("Setting log level to: %s", ll)
log.SetLevel(pl)
} else {
log.SetLevel(log.InfoLevel)
}
for _, n := range []string{"john", "paul", "ringo", "george"} { for _, n := range []string{"john", "paul", "ringo", "george"} {
key := fmt.Sprintf("key_%s_%d", n, ts) key := fmt.Sprintf("key_%s_%d", n, ts)
keys = append(keys, key) keys = append(keys, key)
Loading
@@ -812,6 +980,4 @@ func init() {
Loading
@@ -812,6 +980,4 @@ func init() {
   
log.Printf("Using redis addrs: %#v", addrs) log.Printf("Using redis addrs: %#v", addrs)
defaultRedisHost = RedisHost{Addrs: []string{"redis://" + *redisAddr}, Aliases: aliases} defaultRedisHost = RedisHost{Addrs: []string{"redis://" + *redisAddr}, Aliases: aliases}
TestServerURL = httptest.NewServer(promhttp.Handler()).URL
} }
package main package main
   
import ( import (
"encoding/csv"
"flag" "flag"
"net/http" "net/http"
"os" "os"
"runtime" "runtime"
"strings"
   
"github.com/cloudfoundry-community/go-cfenv"
"github.com/oliver006/redis_exporter/exporter" "github.com/oliver006/redis_exporter/exporter"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
Loading
@@ -30,9 +27,6 @@ var (
Loading
@@ -30,9 +27,6 @@ var (
showVersion = flag.Bool("version", false, "Show version information and exit") showVersion = flag.Bool("version", false, "Show version information and exit")
useCfBindings = flag.Bool("use-cf-bindings", false, "Use Cloud Foundry service bindings") useCfBindings = flag.Bool("use-cf-bindings", false, "Use Cloud Foundry service bindings")
redisMetricsOnly = flag.Bool("redis-only-metrics", false, "Whether to export go runtime metrics also") redisMetricsOnly = flag.Bool("redis-only-metrics", false, "Whether to export go runtime metrics also")
addrs []string
passwords []string
aliases []string
   
// VERSION, BUILD_DATE, GIT_COMMIT are filled in by the build script // VERSION, BUILD_DATE, GIT_COMMIT are filled in by the build script
VERSION = "<<< filled in by build >>>" VERSION = "<<< filled in by build >>>"
Loading
@@ -40,6 +34,13 @@ var (
Loading
@@ -40,6 +34,13 @@ var (
COMMIT_SHA1 = "<<< filled in by build >>>" COMMIT_SHA1 = "<<< filled in by build >>>"
) )
   
func getEnv(key string, defaultVal string) string {
if envVal, ok := os.LookupEnv(key); ok {
return envVal
}
return defaultVal
}
func main() { func main() {
flag.Parse() flag.Parse()
   
Loading
@@ -68,17 +69,19 @@ func main() {
Loading
@@ -68,17 +69,19 @@ func main() {
log.Fatal("Cannot specify both redis.addr and redis.file") log.Fatal("Cannot specify both redis.addr and redis.file")
} }
   
var addrs, passwords, aliases []string
switch { switch {
case *redisFile != "": case *redisFile != "":
var err error var err error
addrs, passwords, aliases, err = loadRedisFile(*redisFile) addrs, passwords, aliases, err = exporter.LoadRedisFile(*redisFile)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
case *useCfBindings: case *useCfBindings:
addrs, passwords, aliases = getCloudFoundryRedisBindings() addrs, passwords, aliases = exporter.GetCloudFoundryRedisBindings()
default: default:
addrs, passwords, aliases = loadRedisArgs(*redisAddr, *redisPassword, *redisAlias, *separator) addrs, passwords, aliases = exporter.LoadRedisArgs(*redisAddr, *redisPassword, *redisAlias, *separator)
} }
   
exp, err := exporter.NewRedisExporter( exp, err := exporter.NewRedisExporter(
Loading
@@ -104,7 +107,7 @@ func main() {
Loading
@@ -104,7 +107,7 @@ func main() {
} else { } else {
prometheus.MustRegister(exp) prometheus.MustRegister(exp)
prometheus.MustRegister(buildInfo) prometheus.MustRegister(buildInfo)
http.Handle(*metricPath, prometheus.Handler()) http.Handle(*metricPath, promhttp.Handler())
} }
   
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
Loading
@@ -124,101 +127,3 @@ func main() {
Loading
@@ -124,101 +127,3 @@ func main() {
log.Printf("Using alias: %#v", aliases) log.Printf("Using alias: %#v", aliases)
log.Fatal(http.ListenAndServe(*listenAddress, nil)) log.Fatal(http.ListenAndServe(*listenAddress, nil))
} }
// loadRedisArgs loads the configuration for which redis hosts to monitor from either
// the environment or as passed from program arguments. Returns the list of host addrs,
// passwords, and their aliases.
func loadRedisArgs(addr, password, alias, separator string) ([]string, []string, []string) {
if addr == "" {
addr = "redis://localhost:6379"
}
addrs = strings.Split(addr, separator)
passwords = strings.Split(password, separator)
for len(passwords) < len(addrs) {
passwords = append(passwords, passwords[0])
}
aliases = strings.Split(alias, separator)
for len(aliases) < len(addrs) {
aliases = append(aliases, aliases[0])
}
return addrs, passwords, aliases
}
// loadRedisFile opens the specified file and loads the configuration for which redis
// hosts to monitor. Returns the list of hosts addrs, passwords, and their aliases.
func loadRedisFile(fileName string) ([]string, []string, []string, error) {
var addrs []string
var passwords []string
var aliases []string
file, err := os.Open(fileName)
if err != nil {
return nil, nil, nil, err
}
r := csv.NewReader(file)
r.FieldsPerRecord = -1
records, err := r.ReadAll()
if err != nil {
return nil, nil, nil, err
}
file.Close()
// For each line, test if it contains an optional password and alias and provide them,
// else give them empty strings
for _, record := range records {
length := len(record)
switch length {
case 3:
addrs = append(addrs, record[0])
passwords = append(passwords, record[1])
aliases = append(aliases, record[2])
case 2:
addrs = append(addrs, record[0])
passwords = append(passwords, record[1])
aliases = append(aliases, "")
case 1:
addrs = append(addrs, record[0])
passwords = append(passwords, "")
aliases = append(aliases, "")
}
}
return addrs, passwords, aliases, nil
}
// getEnv gets an environment variable from a given key and if it doesn't exist,
// returns defaultVal given.
func getEnv(key string, defaultVal string) string {
if envVal, ok := os.LookupEnv(key); ok {
return envVal
}
return defaultVal
}
func getCloudFoundryRedisBindings() (addrs, passwords, aliases []string) {
if !cfenv.IsRunningOnCF() {
return
}
appEnv, err := cfenv.Current()
if err != nil {
log.Warnln("Unable to get current CF environment", err)
return
}
redisServices, err := appEnv.Services.WithTag("redis")
if err != nil {
log.Warnln("Error while getting redis services", err)
return
}
for _, redisService := range redisServices {
credentials := redisService.Credentials
addr := credentials["hostname"].(string) + ":" + credentials["port"].(string)
password := credentials["password"].(string)
alias := redisService.Name
addrs = append(addrs, addr)
passwords = append(passwords, password)
aliases = append(aliases, alias)
}
return
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment