Skip to content

Commit

Permalink
feat: redis ssl in cache options
Browse files Browse the repository at this point in the history
test: fix redis tests

refactor: create options, bump coverage

refactor: test for empty host and port

refactor: move ErrHostMissing to separate file
  • Loading branch information
iwpnd committed Nov 30, 2021
1 parent 8e12766 commit bdce754
Show file tree
Hide file tree
Showing 3 changed files with 270 additions and 15 deletions.
10 changes: 10 additions & 0 deletions cache/redis/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package redis

// ErrHostMissing is raised when Redis Addr is missing Host
type ErrHostMissing struct {
msg string
}

func (error *ErrHostMissing) Error() string {
return error.msg
}
63 changes: 48 additions & 15 deletions cache/redis/redis.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package redis

import (
"crypto/tls"
"fmt"
"net"
"time"

"github.com/go-redis/redis"
Expand All @@ -19,24 +21,26 @@ const (
ConfigKeyDB = "db"
ConfigKeyMaxZoom = "max_zoom"
ConfigKeyTTL = "ttl"
ConfigKeySSL = "ssl"
)

var (
// default values
defaultNetwork = "tcp"
defaultAddress = "127.0.0.1:6379"
defaultPassword = ""
defaultDB = 0
defaultMaxZoom = uint(tegola.MaxZ)
defaultTTL = 0
defaultSSL = false
)

func init() {
cache.Register(CacheType, New)
}

func New(config dict.Dicter) (rcache cache.Interface, err error) {

// default values
defaultNetwork := "tcp"
defaultAddress := "127.0.0.1:6379"
defaultPassword := ""
defaultDB := 0
defaultMaxZoom := uint(tegola.MaxZ)
defaultTTL := 0

c := config

// CreateOptions creates redis.Options from an implicit or explicit c
func CreateOptions(c dict.Dicter) (opts *redis.Options, err error) {
network, err := c.String(ConfigKeyNetwork, &defaultNetwork)
if err != nil {
return nil, err
Expand All @@ -47,6 +51,15 @@ func New(config dict.Dicter) (rcache cache.Interface, err error) {
return nil, err
}

host, _, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}

if host == "" {
return nil, &ErrHostMissing{msg: fmt.Sprintf("no host provided in '%s'", addr)}
}

password, err := c.String(ConfigKeyPassword, &defaultPassword)
if err != nil {
return nil, err
Expand All @@ -57,14 +70,34 @@ func New(config dict.Dicter) (rcache cache.Interface, err error) {
return nil, err
}

client := redis.NewClient(&redis.Options{
o := &redis.Options{
Network: network,
Addr: addr,
Password: password,
DB: db,
PoolSize: 2,
DialTimeout: 3 * time.Second,
})
}

ssl, err := c.Bool(ConfigKeySSL, &defaultSSL)
if err != nil {
return nil, err
}

if ssl {
o.TLSConfig = &tls.Config{ServerName: host}
}

return o, nil
}

func New(c dict.Dicter) (rcache cache.Interface, err error) {
opts, err := CreateOptions(c)
if err != nil {
return nil, err
}

client := redis.NewClient(opts)

pong, err := client.Ping().Result()
if err != nil {
Expand All @@ -74,7 +107,7 @@ func New(config dict.Dicter) (rcache cache.Interface, err error) {
return nil, fmt.Errorf("redis did not respond with 'PONG', '%s'", pong)
}

// the config map's underlying value is int
// the c map's underlying value is int
maxZoom, err := c.Uint(ConfigKeyMaxZoom, &defaultMaxZoom)
if err != nil {
return nil, err
Expand Down
212 changes: 212 additions & 0 deletions cache/redis/redis_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package redis_test

import (
"crypto/tls"
"net"
"os"
"reflect"
"syscall"
"testing"

goredis "github.com/go-redis/redis"
"github.com/go-spatial/tegola/cache"
"github.com/go-spatial/tegola/cache/redis"
"github.com/go-spatial/tegola/dict"
Expand All @@ -16,6 +18,199 @@ import (
// TESTENV is the environment variable that must be set to "yes" to run the redis tests.
const TESTENV = "RUN_REDIS_TESTS"

func TestCreateOptions(t *testing.T) {
ttools.ShouldSkip(t, TESTENV)

type tcase struct {
name string
config dict.Dict
expected *goredis.Options
expectedErr error
}

fn := func(tc tcase) func(*testing.T) {
return func(t *testing.T) {
t.Parallel()

actual, err := redis.CreateOptions(tc.config)
if tc.expectedErr == nil && err != nil {
t.Fatalf("unexpected error: %q", err)
return
}
if tc.expectedErr != nil && err != nil {
if reflect.TypeOf(err) != reflect.TypeOf(tc.expectedErr) {
t.Errorf("invalid error type. expected %T, got %T", tc.expectedErr, err)
return
}
return
}
compareOptions(t, actual, tc.expected)
}
}

tests := map[string]tcase{
"test complete config": {
config: map[string]interface{}{
"network": "tcp",
"address": "127.0.0.1:6379",
"password": "test",
"db": 0,
"max_zoom": uint(10),
"ssl": false,
},
expected: &goredis.Options{
Network: "tcp",
DB: 0,
Addr: "127.0.0.1:6379",
Password: "test",
},
},
"test empty config": {
config: map[string]interface{}{},
expected: &goredis.Options{
Network: "tcp",
DB: 0,
Addr: "127.0.0.1:6379",
Password: "",
},
},
"test ssl config": {
name: "test test ssl config",
config: map[string]interface{}{
"network": "tcp",
"address": "127.0.0.1:6379",
"password": "test",
"db": 0,
"max_zoom": uint(10),
"ssl": true,
},
expected: &goredis.Options{
Network: "tcp",
DB: 0,
Addr: "127.0.0.1:6379",
Password: "test",
TLSConfig: &tls.Config{ /* no deep comparison */ },
},
},
"test bad address": {
name: "test test ssl config",
config: map[string]interface{}{
"network": "tcp",
"address": 2,
"password": "test",
"db": 0,
},
expectedErr: dict.ErrKeyType{
Key: "addr",
Value: 2,
T: reflect.TypeOf(""),
},
},
"test bad host": {
name: "test test ssl config",
config: map[string]interface{}{
"network": "tcp",
"address": "::8080",
"db": 0,
},
expectedErr: &net.AddrError{ /* no deep comparison */ },
},
"test missing host": {
name: "test test ssl config",
config: map[string]interface{}{
"network": "tcp",
"address": ":8080",
"db": 0,
},
expectedErr: &redis.ErrHostMissing{},
},
"test missing port": {
name: "test test ssl config",
config: map[string]interface{}{
"network": "tcp",
"address": "localhost",
"db": 0,
},
expectedErr: &net.AddrError{ /* no deep comparison */ },
},
"test bad db": {
name: "test test ssl config",
config: map[string]interface{}{
"network": "tcp",
"address": "127.0.0.1:6379",
"db": "fails",
},
expectedErr: dict.ErrKeyType{
Key: "db",
Value: "fails",
T: reflect.TypeOf(1),
},
},
"test bad password": {
name: "test test ssl config",
config: map[string]interface{}{
"network": "tcp",
"address": "127.0.0.1:6379",
"password": 0,
},
expectedErr: dict.ErrKeyType{
Key: "password",
Value: 0,
T: reflect.TypeOf(""),
},
},
"test bad network": {
name: "test test ssl config",
config: map[string]interface{}{
"network": 0,
"address": "127.0.0.1:6379",
},
expectedErr: dict.ErrKeyType{
Key: "network",
Value: 0,
T: reflect.TypeOf(1),
},
},
"test bad ssl": {
name: "test test ssl config",
config: map[string]interface{}{
"network": "tcp",
"address": "127.0.0.1:6379",
"ssl": 0,
},
expectedErr: dict.ErrKeyType{
Key: "ssl",
Value: 0,
T: reflect.TypeOf(true),
},
},
}

for name, tc := range tests {
t.Run(name, fn(tc))
}
}

func compareOptions(t *testing.T, actual, expected *goredis.Options) {
t.Helper()

if actual.Addr != expected.Addr {
t.Errorf("got %q, want %q", actual.Addr, expected.Addr)
}
if actual.DB != expected.DB {
t.Errorf("DB: got %q, expected %q", actual.DB, expected.DB)
}
if actual.TLSConfig == nil && expected.TLSConfig != nil {
t.Errorf("got nil TLSConfig, expected a TLSConfig")
}
if actual.TLSConfig != nil && expected.TLSConfig == nil {
t.Errorf("got TLSConfig, expected no TLSConfig")
}
if actual.Password != expected.Password {
t.Errorf("Password: got %q, expected %q", actual.Password, expected.Password)
}
}

// TestNew will run tests against a local redis instance
// on 127.0.0.1:6379
func TestNew(t *testing.T) {
Expand Down Expand Up @@ -76,11 +271,28 @@ func TestNew(t *testing.T) {
"password": "",
"db": 0,
"max_zoom": uint(10),
"ssl": false,
},
},
"implicit config": {
config: map[string]interface{}{},
},
"bad config address": {
config: map[string]interface{}{"address": 0},
expectedErr: dict.ErrKeyType{
Key: "address",
Value: 0,
T: reflect.TypeOf(""),
},
},
"bad config ttl": {
config: map[string]interface{}{"ttl": "fails"},
expectedErr: dict.ErrKeyType{
Key: "ttl",
Value: "fails",
T: reflect.TypeOf(1),
},
},
"bad address": {
config: map[string]interface{}{
"address": "127.0.0.1:6000",
Expand Down

0 comments on commit bdce754

Please sign in to comment.