grafana/pkg/infra/remotecache/redis_storage.go
Carl Bergquist eb507dca89
Remotecache: rename setbytearray/getbytearray to set/get and remove codec (#64470)
Signed-off-by: bergquist <carl.bergquist@gmail.com>
2023-03-10 13:57:29 +01:00

113 lines
3.2 KiB
Go

package remotecache
import (
"context"
"crypto/tls"
"fmt"
"strconv"
"strings"
"time"
"github.com/go-redis/redis/v8"
"github.com/grafana/grafana/pkg/setting"
)
const redisCacheType = "redis"
type redisStorage struct {
c *redis.Client
}
// parseRedisConnStr parses k=v pairs in csv and builds a redis Options object
func parseRedisConnStr(connStr string) (*redis.Options, error) {
keyValueCSV := strings.Split(connStr, ",")
options := &redis.Options{Network: "tcp"}
setTLSIsTrue := false
for _, rawKeyValue := range keyValueCSV {
keyValueTuple := strings.SplitN(rawKeyValue, "=", 2)
if len(keyValueTuple) != 2 {
if strings.HasPrefix(rawKeyValue, "password") {
// don't log the password
rawKeyValue = "password" + setting.RedactedPassword
}
return nil, fmt.Errorf("incorrect redis connection string format detected for '%v', format is key=value,key=value", rawKeyValue)
}
connKey := keyValueTuple[0]
connVal := keyValueTuple[1]
switch connKey {
case "addr":
options.Addr = connVal
case "password":
options.Password = connVal
case "db":
i, err := strconv.Atoi(connVal)
if err != nil {
return nil, fmt.Errorf("%v: %w", "value for db in redis connection string must be a number", err)
}
options.DB = i
case "pool_size":
i, err := strconv.Atoi(connVal)
if err != nil {
return nil, fmt.Errorf("%v: %w", "value for pool_size in redis connection string must be a number", err)
}
options.PoolSize = i
case "ssl":
if connVal != "true" && connVal != "false" && connVal != "insecure" {
return nil, fmt.Errorf("ssl must be set to 'true', 'false', or 'insecure' when present")
}
if connVal == "true" {
setTLSIsTrue = true // Needs addr already parsed, so set later
}
if connVal == "insecure" {
options.TLSConfig = &tls.Config{InsecureSkipVerify: true}
}
default:
return nil, fmt.Errorf("unrecognized option '%v' in redis connection string", connKey)
}
}
if setTLSIsTrue {
// Get hostname from the Addr property and set it on the configuration for TLS
sp := strings.Split(options.Addr, ":")
if len(sp) < 1 {
return nil, fmt.Errorf("unable to get hostname from the addr field, expected host:port, got '%v'", options.Addr)
}
options.TLSConfig = &tls.Config{ServerName: sp[0]}
}
return options, nil
}
func newRedisStorage(opts *setting.RemoteCacheOptions) (*redisStorage, error) {
opt, err := parseRedisConnStr(opts.ConnStr)
if err != nil {
return nil, err
}
return &redisStorage{c: redis.NewClient(opt)}, nil
}
// Set sets value to a given key
func (s *redisStorage) Set(ctx context.Context, key string, data []byte, expires time.Duration) error {
status := s.c.Set(ctx, key, data, expires)
return status.Err()
}
// GetByteArray returns the value as byte array
func (s *redisStorage) Get(ctx context.Context, key string) ([]byte, error) {
return s.c.Get(ctx, key).Bytes()
}
// Delete delete a key from session.
func (s *redisStorage) Delete(ctx context.Context, key string) error {
cmd := s.c.Del(ctx, key)
return cmd.Err()
}
func (s *redisStorage) Count(ctx context.Context, prefix string) (int64, error) {
cmd := s.c.Keys(ctx, prefix+"*")
if cmd.Err() != nil {
return 0, cmd.Err()
}
return int64(len(cmd.Val())), nil
}