redis_storage.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. package remotecache
  2. import (
  3. "crypto/tls"
  4. "fmt"
  5. "strconv"
  6. "strings"
  7. "time"
  8. "github.com/grafana/grafana/pkg/setting"
  9. "github.com/grafana/grafana/pkg/util/errutil"
  10. redis "gopkg.in/redis.v5"
  11. )
  12. const redisCacheType = "redis"
  13. type redisStorage struct {
  14. c *redis.Client
  15. }
  16. // parseRedisConnStr parses k=v pairs in csv and builds a redis Options object
  17. func parseRedisConnStr(connStr string) (*redis.Options, error) {
  18. keyValueCSV := strings.Split(connStr, ",")
  19. options := &redis.Options{Network: "tcp"}
  20. setTLSIsTrue := false
  21. for _, rawKeyValue := range keyValueCSV {
  22. keyValueTuple := strings.SplitN(rawKeyValue, "=", 2)
  23. if len(keyValueTuple) != 2 {
  24. if strings.HasPrefix(rawKeyValue, "password") {
  25. // don't log the password
  26. rawKeyValue = "password******"
  27. }
  28. return nil, fmt.Errorf("incorrect redis connection string format detected for '%v', format is key=value,key=value", rawKeyValue)
  29. }
  30. connKey := keyValueTuple[0]
  31. connVal := keyValueTuple[1]
  32. switch connKey {
  33. case "addr":
  34. options.Addr = connVal
  35. case "password":
  36. options.Password = connVal
  37. case "db":
  38. i, err := strconv.Atoi(connVal)
  39. if err != nil {
  40. return nil, errutil.Wrap("value for db in redis connection string must be a number", err)
  41. }
  42. options.DB = i
  43. case "pool_size":
  44. i, err := strconv.Atoi(connVal)
  45. if err != nil {
  46. return nil, errutil.Wrap("value for pool_size in redis connection string must be a number", err)
  47. }
  48. options.PoolSize = i
  49. case "ssl":
  50. if connVal != "true" && connVal != "false" && connVal != "insecure" {
  51. return nil, fmt.Errorf("ssl must be set to 'true', 'false', or 'insecure' when present")
  52. }
  53. if connVal == "true" {
  54. setTLSIsTrue = true // Needs addr already parsed, so set later
  55. }
  56. if connVal == "insecure" {
  57. options.TLSConfig = &tls.Config{InsecureSkipVerify: true}
  58. }
  59. default:
  60. return nil, fmt.Errorf("unrecognized option '%v' in redis connection string", connKey)
  61. }
  62. }
  63. if setTLSIsTrue {
  64. // Get hostname from the Addr property and set it on the configuration for TLS
  65. sp := strings.Split(options.Addr, ":")
  66. if len(sp) < 1 {
  67. return nil, fmt.Errorf("unable to get hostname from the addr field, expected host:port, got '%v'", options.Addr)
  68. }
  69. options.TLSConfig = &tls.Config{ServerName: sp[0]}
  70. }
  71. return options, nil
  72. }
  73. func newRedisStorage(opts *setting.RemoteCacheOptions) (*redisStorage, error) {
  74. opt, err := parseRedisConnStr(opts.ConnStr)
  75. if err != nil {
  76. return nil, err
  77. }
  78. return &redisStorage{c: redis.NewClient(opt)}, nil
  79. }
  80. // Set sets value to given key in session.
  81. func (s *redisStorage) Set(key string, val interface{}, expires time.Duration) error {
  82. item := &cachedItem{Val: val}
  83. value, err := encodeGob(item)
  84. if err != nil {
  85. return err
  86. }
  87. status := s.c.Set(key, string(value), expires)
  88. return status.Err()
  89. }
  90. // Get gets value by given key in session.
  91. func (s *redisStorage) Get(key string) (interface{}, error) {
  92. v := s.c.Get(key)
  93. item := &cachedItem{}
  94. err := decodeGob([]byte(v.Val()), item)
  95. if err == nil {
  96. return item.Val, nil
  97. }
  98. if err.Error() == "EOF" {
  99. return nil, ErrCacheItemNotFound
  100. }
  101. return nil, err
  102. }
  103. // Delete delete a key from session.
  104. func (s *redisStorage) Delete(key string) error {
  105. cmd := s.c.Del(key)
  106. return cmd.Err()
  107. }