diff --git a/.drone.yml b/.drone.yml index 7199cd9e..1b37bf37 100644 --- a/.drone.yml +++ b/.drone.yml @@ -28,6 +28,13 @@ services: ports: - 6380 + - name: pwd-redis6 + image: redis:6 + commands: + - "/usr/local/bin/redis-server --port 6390 --requirepass dummy --user exporter on +INFO +SELECT +SLOWLOG +LATENCY '>exporter-password' --dbfilename dump6-pwd.rdb" + ports: + - 6380 + - name: redis-2-8 image: redis:2.8 commands: @@ -72,7 +79,8 @@ steps: TEST_REDIS_2_8_URI: "redis://redis-2-8:6381" TEST_KEYDB01_URI: "redis://keydb-01:6401" TEST_KEYDB02_URI: "redis://keydb-02:6402" - TEST_PWD_REDIS_URI: "redis://h:redis-password@pwd-redis5:6380" + TEST_PWD_REDIS_URI: "redis://:redis-password@pwd-redis5:6380" + TEST_USER_PWD_REDIS_URI: "redis://exporter:exporter-password@pwd-redis6:6390" TEST_REDIS_CLUSTER_MASTER_URI: "redis://redis-cluster:7000" TEST_REDIS_CLUSTER_SLAVE_URI: "redis://redis-cluster:7005" TEST_TILE38_URI: "redis://tile38:9851" diff --git a/README.md b/README.md index 3d1e0307..bd17b67e 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,7 @@ Prometheus uses file watches and all changes to the json file are applied immedi Name | Environment Variable Name | Description -----------------------|--------------------------------------|----------------- redis.addr | REDIS_ADDR | Address of the Redis instance, defaults to `redis://localhost:6379`. +redis.user | REDIS_USER | User name to use for authentication (Redis ACL for Redis 6.0 and newer). redis.password | REDIS_PASSWORD | Password of the Redis instance, defaults to `""` (no password). check-keys | REDIS_EXPORTER_CHECK_KEYS | Comma separated list of key patterns to export value and length/size, eg: `db3=user_count` will export key `user_count` from db `3`. db defaults to `0` if omitted. The key patterns specified with this flag will be found using [SCAN](https://redis.io/commands/scan). Use this option if you need glob pattern matching; `check-single-keys` is faster for non-pattern keys. Warning: using `--check-keys` to match a very large number of keys can slow down the exporter to the point where it doesn't finish scraping the redis instance. check-single-keys | REDIS_EXPORTER_CHECK_SINGLE_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. The keys specified with this flag will be looked up directly without any glob pattern matching. Use this option if you don't need glob pattern matching; it is faster than `check-keys`. diff --git a/exporter.go b/exporter.go index 47f342b9..3111ba65 100644 --- a/exporter.go +++ b/exporter.go @@ -50,6 +50,7 @@ type Exporter struct { } type Options struct { + User string Password string Namespace string ConfigCommandName string @@ -1189,6 +1190,10 @@ func (e *Exporter) connectToRedis() (redis.Conn, error) { }), } + if e.options.User != "" { + options = append(options, redis.DialUsername(e.options.User)) + } + if e.options.Password != "" { options = append(options, redis.DialPassword(e.options.Password)) } @@ -1197,6 +1202,7 @@ func (e *Exporter) connectToRedis() (redis.Conn, error) { if !strings.Contains(uri, "://") { uri = "redis://" + uri } + log.Debugf("Trying DialURL(): %s", uri) c, err := redis.DialURL(uri, options...) if err != nil { diff --git a/exporter_test.go b/exporter_test.go index 58bdf3fb..d91a6e58 100644 --- a/exporter_test.go +++ b/exporter_test.go @@ -1251,26 +1251,33 @@ func TestClusterMaster(t *testing.T) { } func TestPasswordProtectedInstance(t *testing.T) { - if os.Getenv("TEST_PWD_REDIS_URI") == "" { - t.Skipf("TEST_PWD_REDIS_URI not set - skipping") + uriEnvs := []string{ + "TEST_PWD_REDIS_URI", + "TEST_USER_PWD_REDIS_URI", } - uri := os.Getenv("TEST_PWD_REDIS_URI") - setupDBKeys(t, uri) - e, _ := NewRedisExporter(uri, Options{Namespace: "test", Registry: prometheus.NewRegistry()}) - ts := httptest.NewServer(e) - defer ts.Close() + for _, uriEnvName := range uriEnvs { + if os.Getenv(uriEnvName) == "" { + t.Logf("%s not set - skipping", uriEnvName) + continue + } - chM := make(chan prometheus.Metric, 10000) - go func() { - e.Collect(chM) - close(chM) - }() + uri := os.Getenv(uriEnvName) - body := downloadURL(t, ts.URL+"/metrics") + e, _ := NewRedisExporter(uri, Options{Namespace: "test", Registry: prometheus.NewRegistry()}) + ts := httptest.NewServer(e) + defer ts.Close() + + chM := make(chan prometheus.Metric, 10000) + go func() { + e.Collect(chM) + close(chM) + }() - if !strings.Contains(body, "test_up") { - t.Errorf("error, missing test_up") + body := downloadURL(t, ts.URL+"/metrics") + if !strings.Contains(body, "test_up") { + t.Errorf("%s - response to /metric doesn't contain test_up", uriEnvName) + } } } diff --git a/go.mod b/go.mod index 5e22518f..0e81c666 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/oliver006/redis_exporter go 1.13 require ( - github.com/gomodule/redigo v2.0.1-0.20200211073029-7ac8ae1ada9f+incompatible + github.com/gomodule/redigo v1.8.2 github.com/prometheus/client_golang v1.6.0 github.com/prometheus/client_model v0.2.0 github.com/sirupsen/logrus v1.6.0 diff --git a/go.sum b/go.sum index 6dede422..58b58a76 100644 --- a/go.sum +++ b/go.sum @@ -29,8 +29,8 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/gomodule/redigo v2.0.1-0.20200211073029-7ac8ae1ada9f+incompatible h1:7uFn3ABPk3VN7vbcd4KFo3PExEw/O5/MuxB6ofAkUeQ= -github.com/gomodule/redigo v2.0.1-0.20200211073029-7ac8ae1ada9f+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= +github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= @@ -87,6 +87,8 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/main.go b/main.go index e19c287e..51738ed0 100644 --- a/main.go +++ b/main.go @@ -43,6 +43,7 @@ func getEnvBool(key string, defaultVal bool) bool { func main() { var ( redisAddr = flag.String("redis.addr", getEnv("REDIS_ADDR", "redis://localhost:6379"), "Address of the Redis instance to scrape") + redisUser = flag.String("redis.user", getEnv("REDIS_USER", ""), "User name to use for authentication (Redis ACL for Redis 6.0 and newer)") redisPwd = flag.String("redis.password", getEnv("REDIS_PASSWORD", ""), "Password of the Redis instance to scrape") namespace = flag.String("namespace", getEnv("REDIS_EXPORTER_NAMESPACE", "redis"), "Namespace for metrics") checkKeys = flag.String("check-keys", getEnv("REDIS_EXPORTER_CHECK_KEYS", ""), "Comma separated list of key-patterns to export value and length/size, searched for with SCAN") @@ -122,6 +123,7 @@ func main() { exp, err := NewRedisExporter( *redisAddr, Options{ + User: *redisUser, Password: *redisPwd, Namespace: *namespace, ConfigCommandName: *configCommand,