mirror of
https://github.com/containous/traefik.git
synced 2024-12-22 13:34:03 +03:00
Add Redis Sentinel support
Co-authored-by: Romain <rtribotte@users.noreply.github.com>
This commit is contained in:
parent
60123a8f3f
commit
3dfaa3d5fa
@ -229,3 +229,166 @@ providers:
|
||||
```bash tab="CLI"
|
||||
--providers.redis.tls.insecureSkipVerify=true
|
||||
```
|
||||
|
||||
### `sentinel`
|
||||
|
||||
_Optional_
|
||||
|
||||
Defines the Sentinel configuration used to interact with Redis Sentinel.
|
||||
|
||||
#### `masterName`
|
||||
|
||||
_Required_
|
||||
|
||||
`masterName` is the name of the Sentinel master.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
redis:
|
||||
sentinel:
|
||||
masterName: my-master
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.redis.sentinel]
|
||||
masterName = "my-master"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.redis.sentinel.masterName=my-master
|
||||
```
|
||||
|
||||
#### `username`
|
||||
|
||||
_Optional_
|
||||
|
||||
`username` is the username for Sentinel authentication.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
redis:
|
||||
sentinel:
|
||||
username: user
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.redis.sentinel]
|
||||
username = "user"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.redis.sentinel.username=user
|
||||
```
|
||||
|
||||
#### `password`
|
||||
|
||||
_Optional_
|
||||
|
||||
`password` is the password for Sentinel authentication.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
redis:
|
||||
sentinel:
|
||||
password: password
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.redis.sentinel]
|
||||
password = "password"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.redis.sentinel.password=password
|
||||
```
|
||||
|
||||
#### `latencyStrategy`
|
||||
|
||||
_Optional, Default=false_
|
||||
|
||||
`latencyStrategy` defines whether to route commands to the closest master or replica nodes
|
||||
(mutually exclusive with RandomStrategy and ReplicaStrategy).
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
redis:
|
||||
sentinel:
|
||||
latencyStrategy: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.redis.sentinel]
|
||||
latencyStrategy = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.redis.sentinel.latencyStrategy=true
|
||||
```
|
||||
|
||||
#### `randomStrategy`
|
||||
|
||||
_Optional, Default=false_
|
||||
|
||||
`randomStrategy` defines whether to route commands randomly to master or replica nodes
|
||||
(mutually exclusive with LatencyStrategy and ReplicaStrategy).
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
redis:
|
||||
sentinel:
|
||||
randomStrategy: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.redis.sentinel]
|
||||
randomStrategy = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.redis.sentinel.randomStrategy=true
|
||||
```
|
||||
|
||||
#### `replicaStrategy`
|
||||
|
||||
_Optional, Default=false_
|
||||
|
||||
`replicaStrategy` Defines whether to route all commands to replica nodes
|
||||
(mutually exclusive with LatencyStrategy and RandomStrategy).
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
redis:
|
||||
sentinel:
|
||||
replicaStrategy: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.redis.sentinel]
|
||||
replicaStrategy = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.redis.sentinel.replicaStrategy=true
|
||||
```
|
||||
|
||||
#### `useDisconnectedReplicas`
|
||||
|
||||
_Optional, Default=false_
|
||||
|
||||
`useDisconnectedReplicas` defines whether to use replicas disconnected with master when cannot get connected replicas.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
redis:
|
||||
sentinel:
|
||||
useDisconnectedReplicas: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.redis.sentinel]
|
||||
useDisconnectedReplicas = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.redis.sentinel.useDisconnectedReplicas=true
|
||||
```
|
||||
|
@ -906,6 +906,27 @@ Password for authentication.
|
||||
`--providers.redis.rootkey`:
|
||||
Root key used for KV store. (Default: ```traefik```)
|
||||
|
||||
`--providers.redis.sentinel.latencystrategy`:
|
||||
Defines whether to route commands to the closest master or replica nodes (mutually exclusive with RandomStrategy and ReplicaStrategy). (Default: ```false```)
|
||||
|
||||
`--providers.redis.sentinel.mastername`:
|
||||
Name of the master.
|
||||
|
||||
`--providers.redis.sentinel.password`:
|
||||
Password for Sentinel authentication.
|
||||
|
||||
`--providers.redis.sentinel.randomstrategy`:
|
||||
Defines whether to route commands randomly to master or replica nodes (mutually exclusive with LatencyStrategy and ReplicaStrategy). (Default: ```false```)
|
||||
|
||||
`--providers.redis.sentinel.replicastrategy`:
|
||||
Defines whether to route all commands to replica nodes (mutually exclusive with LatencyStrategy and RandomStrategy). (Default: ```false```)
|
||||
|
||||
`--providers.redis.sentinel.usedisconnectedreplicas`:
|
||||
Use replicas disconnected with master when cannot get connected replicas. (Default: ```false```)
|
||||
|
||||
`--providers.redis.sentinel.username`:
|
||||
Username for Sentinel authentication.
|
||||
|
||||
`--providers.redis.tls.ca`:
|
||||
TLS CA
|
||||
|
||||
|
@ -906,6 +906,27 @@ Password for authentication.
|
||||
`TRAEFIK_PROVIDERS_REDIS_ROOTKEY`:
|
||||
Root key used for KV store. (Default: ```traefik```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_REDIS_SENTINEL_LATENCYSTRATEGY`:
|
||||
Defines whether to route commands to the closest master or replica nodes (mutually exclusive with RandomStrategy and ReplicaStrategy). (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_REDIS_SENTINEL_MASTERNAME`:
|
||||
Name of the master.
|
||||
|
||||
`TRAEFIK_PROVIDERS_REDIS_SENTINEL_PASSWORD`:
|
||||
Password for Sentinel authentication.
|
||||
|
||||
`TRAEFIK_PROVIDERS_REDIS_SENTINEL_RANDOMSTRATEGY`:
|
||||
Defines whether to route commands randomly to master or replica nodes (mutually exclusive with LatencyStrategy and ReplicaStrategy). (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_REDIS_SENTINEL_REPLICASTRATEGY`:
|
||||
Defines whether to route all commands to replica nodes (mutually exclusive with LatencyStrategy and RandomStrategy). (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_REDIS_SENTINEL_USEDISCONNECTEDREPLICAS`:
|
||||
Use replicas disconnected with master when cannot get connected replicas. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_REDIS_SENTINEL_USERNAME`:
|
||||
Username for Sentinel authentication.
|
||||
|
||||
`TRAEFIK_PROVIDERS_REDIS_TLS_CA`:
|
||||
TLS CA
|
||||
|
||||
|
@ -247,6 +247,14 @@
|
||||
cert = "foobar"
|
||||
key = "foobar"
|
||||
insecureSkipVerify = true
|
||||
[providers.redis.sentinel]
|
||||
masterName = "foobar"
|
||||
username = "foobar"
|
||||
password = "foobar"
|
||||
latencyStrategy = true
|
||||
randomStrategy = true
|
||||
replicaStrategy = true
|
||||
useDisconnectedReplicas = true
|
||||
[providers.http]
|
||||
endpoint = "foobar"
|
||||
pollInterval = "42s"
|
||||
|
@ -275,6 +275,14 @@ providers:
|
||||
cert: foobar
|
||||
key: foobar
|
||||
insecureSkipVerify: true
|
||||
sentinel:
|
||||
masterName: foobar
|
||||
username: foobar
|
||||
password: foobar
|
||||
latencyStrategy: true
|
||||
randomStrategy: true
|
||||
replicaStrategy: true
|
||||
useDisconnectedReplicas: true
|
||||
http:
|
||||
endpoint: foobar
|
||||
pollInterval: 42s
|
||||
|
5
go.mod
5
go.mod
@ -37,7 +37,7 @@ require (
|
||||
github.com/klauspost/compress v1.17.1
|
||||
github.com/kvtools/consul v1.0.2
|
||||
github.com/kvtools/etcdv3 v1.0.2
|
||||
github.com/kvtools/redis v1.0.2
|
||||
github.com/kvtools/redis v1.1.0
|
||||
github.com/kvtools/valkeyrie v1.0.0
|
||||
github.com/kvtools/zookeeper v1.0.2
|
||||
github.com/mailgun/ttlmap v0.0.0-20170619185759-c1c17f74874f
|
||||
@ -189,7 +189,6 @@ require (
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||
github.com/go-openapi/swag v0.19.14 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||
github.com/go-resty/resty/v2 v2.7.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/go-zookeeper/zk v1.0.3 // indirect
|
||||
@ -278,6 +277,7 @@ require (
|
||||
github.com/nrdcg/nodion v0.1.0 // indirect
|
||||
github.com/nrdcg/porkbun v0.2.0 // indirect
|
||||
github.com/nzdjb/go-metaname v1.0.0 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
@ -294,6 +294,7 @@ require (
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4 // indirect
|
||||
github.com/redis/go-redis/v9 v9.2.1 // indirect
|
||||
github.com/sacloud/api-client-go v0.2.8 // indirect
|
||||
github.com/sacloud/go-http v0.1.6 // indirect
|
||||
github.com/sacloud/iaas-api-go v1.11.1 // indirect
|
||||
|
12
go.sum
12
go.sum
@ -287,6 +287,10 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
|
||||
github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/buger/goterm v1.0.0 h1:ZB6uUlY8+sjJyFGzz2WpRqX2XYPeXVgtZAOJMwOsTWM=
|
||||
github.com/buger/goterm v1.0.0/go.mod h1:16STi3LquiscTIHA8SXUNKEa/Cnu4ZHBH8NsCaWgso0=
|
||||
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
@ -741,8 +745,6 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
|
||||
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
|
||||
github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
@ -1169,8 +1171,8 @@ github.com/kvtools/consul v1.0.2 h1:ltPgs4Ld09Xaa7zrOJ/TewBYKAsr11/LRFpErdkb8AA=
|
||||
github.com/kvtools/consul v1.0.2/go.mod h1:bFnzfGJ5ZIRRXCBGBmwhJlLdEWOlrjOcS1WjyAQzaJA=
|
||||
github.com/kvtools/etcdv3 v1.0.2 h1:EB0mAtzqe1folE7m7Q6wnCXcGwaOmrYmsVmF3hNsTKI=
|
||||
github.com/kvtools/etcdv3 v1.0.2/go.mod h1:Xr6DbwqjuCEcXAIWmXxw0DX+N5BhuvablXgN90XeqMM=
|
||||
github.com/kvtools/redis v1.0.2 h1:D3GjGGtssJF2w8mniWtIxcT/YX9YnRc4jNCm0hrVygQ=
|
||||
github.com/kvtools/redis v1.0.2/go.mod h1:wuUNwwKOHi2TYxDxj1sGF74Jdg0jywydnatXtnOR3hA=
|
||||
github.com/kvtools/redis v1.1.0 h1:nXRAyh2nsaWiJyrX449/qHMc3SvGUqRqRXcrA/MplEo=
|
||||
github.com/kvtools/redis v1.1.0/go.mod h1:cqg3esJOIYMQ1qy5LVIbPZz9kuiBBcFREP2N5b9+Dn0=
|
||||
github.com/kvtools/valkeyrie v1.0.0 h1:LAITop2wPoYCMitR24GZZsW0b57hmI+ePD18VRTtOf0=
|
||||
github.com/kvtools/valkeyrie v1.0.0/go.mod h1:bDi/OdhJCSbGPMsCgUQl881yuEweKCSItAtTBI+ZjpU=
|
||||
github.com/kvtools/zookeeper v1.0.2 h1:uK0CzQa+mtKGxDDH+DeqXo2HC1Kx4hWXZ7pX/zS4aTo=
|
||||
@ -1586,6 +1588,8 @@ github.com/rancher/go-rancher-metadata v0.0.0-20200311180630-7f4c936a06ac/go.mod
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg=
|
||||
github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052 h1:Qp27Idfgi6ACvFQat5+VJvlYToylpM/hcyLBI3WaKPA=
|
||||
github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052/go.mod h1:uvX/8buq8uVeiZiFht+0lqSLBHF+uGV8BrTv8W/SIwk=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
|
19
integration/fixtures/redis/sentinel.toml
Normal file
19
integration/fixtures/redis/sentinel.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[global]
|
||||
checkNewVersion = false
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
|
||||
[entryPoints.web]
|
||||
address = ":8000"
|
||||
|
||||
[api]
|
||||
insecure = true
|
||||
|
||||
[providers.redis]
|
||||
rootKey = "traefik"
|
||||
endpoints = ["{{ .RedisAddress }}"]
|
||||
|
||||
[providers.redis.sentinel]
|
||||
masterName = "mymaster"
|
@ -4,12 +4,18 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/structs"
|
||||
"github.com/go-check/check"
|
||||
"github.com/kvtools/redis"
|
||||
"github.com/kvtools/valkeyrie"
|
||||
@ -23,24 +29,36 @@ import (
|
||||
// Redis test suites.
|
||||
type RedisSuite struct {
|
||||
BaseSuite
|
||||
kvClient store.Store
|
||||
redisAddr string
|
||||
kvClient store.Store
|
||||
redisEndpoints []string
|
||||
}
|
||||
|
||||
func (s *RedisSuite) TearDownSuite(c *check.C) {
|
||||
s.composeDown(c)
|
||||
|
||||
for _, filename := range []string{"sentinel1.conf", "sentinel2.conf", "sentinel3.conf"} {
|
||||
err := os.Remove(filepath.Join(".", "resources", "compose", "config", filename))
|
||||
if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||
c.Fatal("unable to clean configuration file for sentinel: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RedisSuite) setupStore(c *check.C) {
|
||||
s.createComposeProject(c, "redis")
|
||||
s.composeUp(c)
|
||||
|
||||
s.redisAddr = net.JoinHostPort(s.getComposeServiceIP(c, "redis"), "6379")
|
||||
s.redisEndpoints = []string{}
|
||||
s.redisEndpoints = append(s.redisEndpoints, net.JoinHostPort(s.getComposeServiceIP(c, "redis"), "6379"))
|
||||
|
||||
kv, err := valkeyrie.NewStore(
|
||||
context.Background(),
|
||||
redis.StoreName,
|
||||
[]string{s.redisAddr},
|
||||
s.redisEndpoints,
|
||||
&redis.Config{},
|
||||
)
|
||||
if err != nil {
|
||||
c.Fatal("Cannot create store redis")
|
||||
c.Fatal("Cannot create store redis: ", err)
|
||||
}
|
||||
s.kvClient = kv
|
||||
|
||||
@ -52,7 +70,173 @@ func (s *RedisSuite) setupStore(c *check.C) {
|
||||
func (s *RedisSuite) TestSimpleConfiguration(c *check.C) {
|
||||
s.setupStore(c)
|
||||
|
||||
file := s.adaptFile(c, "fixtures/redis/simple.toml", struct{ RedisAddress string }{s.redisAddr})
|
||||
file := s.adaptFile(c, "fixtures/redis/simple.toml", struct{ RedisAddress string }{
|
||||
RedisAddress: strings.Join(s.redisEndpoints, ","),
|
||||
})
|
||||
defer os.Remove(file)
|
||||
|
||||
data := map[string]string{
|
||||
"traefik/http/routers/Router0/entryPoints/0": "web",
|
||||
"traefik/http/routers/Router0/middlewares/0": "compressor",
|
||||
"traefik/http/routers/Router0/middlewares/1": "striper",
|
||||
"traefik/http/routers/Router0/service": "simplesvc",
|
||||
"traefik/http/routers/Router0/rule": "Host(`kv1.localhost`)",
|
||||
"traefik/http/routers/Router0/priority": "42",
|
||||
"traefik/http/routers/Router0/tls": "true",
|
||||
|
||||
"traefik/http/routers/Router1/rule": "Host(`kv2.localhost`)",
|
||||
"traefik/http/routers/Router1/priority": "42",
|
||||
"traefik/http/routers/Router1/tls/domains/0/main": "aaa.localhost",
|
||||
"traefik/http/routers/Router1/tls/domains/0/sans/0": "aaa.aaa.localhost",
|
||||
"traefik/http/routers/Router1/tls/domains/0/sans/1": "bbb.aaa.localhost",
|
||||
"traefik/http/routers/Router1/tls/domains/1/main": "bbb.localhost",
|
||||
"traefik/http/routers/Router1/tls/domains/1/sans/0": "aaa.bbb.localhost",
|
||||
"traefik/http/routers/Router1/tls/domains/1/sans/1": "bbb.bbb.localhost",
|
||||
"traefik/http/routers/Router1/entryPoints/0": "web",
|
||||
"traefik/http/routers/Router1/service": "simplesvc",
|
||||
|
||||
"traefik/http/services/simplesvc/loadBalancer/servers/0/url": "http://10.0.1.1:8888",
|
||||
"traefik/http/services/simplesvc/loadBalancer/servers/1/url": "http://10.0.1.1:8889",
|
||||
|
||||
"traefik/http/services/srvcA/loadBalancer/servers/0/url": "http://10.0.1.2:8888",
|
||||
"traefik/http/services/srvcA/loadBalancer/servers/1/url": "http://10.0.1.2:8889",
|
||||
|
||||
"traefik/http/services/srvcB/loadBalancer/servers/0/url": "http://10.0.1.3:8888",
|
||||
"traefik/http/services/srvcB/loadBalancer/servers/1/url": "http://10.0.1.3:8889",
|
||||
|
||||
"traefik/http/services/mirror/mirroring/service": "simplesvc",
|
||||
"traefik/http/services/mirror/mirroring/mirrors/0/name": "srvcA",
|
||||
"traefik/http/services/mirror/mirroring/mirrors/0/percent": "42",
|
||||
"traefik/http/services/mirror/mirroring/mirrors/1/name": "srvcB",
|
||||
"traefik/http/services/mirror/mirroring/mirrors/1/percent": "42",
|
||||
|
||||
"traefik/http/services/Service03/weighted/services/0/name": "srvcA",
|
||||
"traefik/http/services/Service03/weighted/services/0/weight": "42",
|
||||
"traefik/http/services/Service03/weighted/services/1/name": "srvcB",
|
||||
"traefik/http/services/Service03/weighted/services/1/weight": "42",
|
||||
|
||||
"traefik/http/middlewares/compressor/compress": "true",
|
||||
"traefik/http/middlewares/striper/stripPrefix/prefixes/0": "foo",
|
||||
"traefik/http/middlewares/striper/stripPrefix/prefixes/1": "bar",
|
||||
"traefik/http/middlewares/striper/stripPrefix/forceSlash": "true",
|
||||
}
|
||||
|
||||
for k, v := range data {
|
||||
err := s.kvClient.Put(context.Background(), k, []byte(v), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
defer display(c)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer s.killCmd(cmd)
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
|
||||
try.BodyContains(`"striper@redis":`, `"compressor@redis":`, `"srvcA@redis":`, `"srvcB@redis":`),
|
||||
)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
resp, err := http.Get("http://127.0.0.1:8080/api/rawdata")
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
var obtained api.RunTimeRepresentation
|
||||
err = json.NewDecoder(resp.Body).Decode(&obtained)
|
||||
c.Assert(err, checker.IsNil)
|
||||
got, err := json.MarshalIndent(obtained, "", " ")
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
expectedJSON := filepath.FromSlash("testdata/rawdata-redis.json")
|
||||
|
||||
if *updateExpected {
|
||||
err = os.WriteFile(expectedJSON, got, 0o666)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
expected, err := os.ReadFile(expectedJSON)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
if !bytes.Equal(expected, got) {
|
||||
diff := difflib.UnifiedDiff{
|
||||
FromFile: "Expected",
|
||||
A: difflib.SplitLines(string(expected)),
|
||||
ToFile: "Got",
|
||||
B: difflib.SplitLines(string(got)),
|
||||
Context: 3,
|
||||
}
|
||||
|
||||
text, err := difflib.GetUnifiedDiffString(diff)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Error(text)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RedisSuite) setupSentinelStore(c *check.C) {
|
||||
s.setupSentinelConfiguration(c, []string{"26379", "36379", "46379"})
|
||||
|
||||
s.createComposeProject(c, "redis_sentinel")
|
||||
s.composeUp(c)
|
||||
|
||||
s.redisEndpoints = []string{
|
||||
net.JoinHostPort(s.getComposeServiceIP(c, "sentinel1"), "26379"),
|
||||
net.JoinHostPort(s.getComposeServiceIP(c, "sentinel2"), "36379"),
|
||||
net.JoinHostPort(s.getComposeServiceIP(c, "sentinel3"), "46379"),
|
||||
}
|
||||
|
||||
kv, err := valkeyrie.NewStore(
|
||||
context.Background(),
|
||||
redis.StoreName,
|
||||
s.redisEndpoints,
|
||||
&redis.Config{
|
||||
Sentinel: &redis.Sentinel{
|
||||
MasterName: "mymaster",
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
c.Fatal("Cannot create store redis sentinel")
|
||||
}
|
||||
s.kvClient = kv
|
||||
|
||||
// wait for redis
|
||||
err = try.Do(60*time.Second, try.KVExists(kv, "test"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *RedisSuite) setupSentinelConfiguration(c *check.C, ports []string) {
|
||||
for i, port := range ports {
|
||||
templateValue := struct{ SentinelPort string }{SentinelPort: port}
|
||||
|
||||
// Load file
|
||||
templateFile := "resources/compose/config/sentinel_template.conf"
|
||||
tmpl, err := template.ParseFiles(templateFile)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
folder, prefix := filepath.Split(templateFile)
|
||||
|
||||
fileName := fmt.Sprintf("%s/sentinel%d.conf", folder, i+1)
|
||||
tmpFile, err := os.Create(fileName)
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer tmpFile.Close()
|
||||
|
||||
model := structs.Map(templateValue)
|
||||
model["SelfFilename"] = tmpFile.Name()
|
||||
|
||||
err = tmpl.ExecuteTemplate(tmpFile, prefix, model)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = tmpFile.Sync()
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RedisSuite) TestSentinelConfiguration(c *check.C) {
|
||||
s.setupSentinelStore(c)
|
||||
|
||||
file := s.adaptFile(c, "fixtures/redis/sentinel.toml", struct{ RedisAddress string }{
|
||||
RedisAddress: strings.Join(s.redisEndpoints, `","`),
|
||||
})
|
||||
defer os.Remove(file)
|
||||
|
||||
data := map[string]string{
|
||||
|
@ -0,0 +1,5 @@
|
||||
port {{ .SentinelPort }}
|
||||
dir "/tmp"
|
||||
sentinel resolve-hostnames yes
|
||||
sentinel monitor mymaster master 6380 2
|
||||
sentinel deny-scripts-reconfig yes
|
61
integration/resources/compose/redis_sentinel.yml
Normal file
61
integration/resources/compose/redis_sentinel.yml
Normal file
@ -0,0 +1,61 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
master:
|
||||
image: redis
|
||||
container_name: redis-master
|
||||
command: redis-server --port 6380
|
||||
ports:
|
||||
- 6380:6380
|
||||
healthcheck:
|
||||
test: redis-cli -p 6380 ping
|
||||
node1:
|
||||
image: redis
|
||||
container_name: redis-node-1
|
||||
ports:
|
||||
- 6381:6381
|
||||
command: redis-server --port 6381 --slaveof redis-master 6380
|
||||
healthcheck:
|
||||
test: redis-cli -p 6381 ping
|
||||
node2:
|
||||
image: redis
|
||||
container_name: redis-node-2
|
||||
ports:
|
||||
- 6382:6382
|
||||
command: redis-server --port 6382 --slaveof redis-master 6380
|
||||
healthcheck:
|
||||
test: redis-cli -p 6382 ping
|
||||
sentinel1:
|
||||
image: redis
|
||||
container_name: redis-sentinel-1
|
||||
ports:
|
||||
- 26379:26379
|
||||
command: redis-sentinel /usr/local/etc/redis/conf/sentinel1.conf
|
||||
healthcheck:
|
||||
test: redis-cli -p 26379 ping
|
||||
volumes:
|
||||
- ./resources/compose/config:/usr/local/etc/redis/conf
|
||||
sentinel2:
|
||||
image: redis
|
||||
container_name: redis-sentinel-2
|
||||
ports:
|
||||
- 36379:26379
|
||||
command: redis-sentinel /usr/local/etc/redis/conf/sentinel2.conf
|
||||
healthcheck:
|
||||
test: redis-cli -p 36379 ping
|
||||
volumes:
|
||||
- ./resources/compose/config:/usr/local/etc/redis/conf
|
||||
sentinel3:
|
||||
image: redis
|
||||
container_name: redis-sentinel-3
|
||||
ports:
|
||||
- 46379:26379
|
||||
command: redis-sentinel /usr/local/etc/redis/conf/sentinel3.conf
|
||||
healthcheck:
|
||||
test: redis-cli -p 46379 ping
|
||||
volumes:
|
||||
- ./resources/compose/config:/usr/local/etc/redis/conf
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: traefik-test-network
|
||||
external: true
|
@ -2,6 +2,7 @@ package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/kvtools/redis"
|
||||
@ -20,6 +21,20 @@ type Provider struct {
|
||||
Username string `description:"Username for authentication." json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty" loggable:"false"`
|
||||
Password string `description:"Password for authentication." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty" loggable:"false"`
|
||||
DB int `description:"Database to be selected after connecting to the server." json:"db,omitempty" toml:"db,omitempty" yaml:"db,omitempty"`
|
||||
Sentinel *Sentinel `description:"Enable Sentinel support." json:"sentinel,omitempty" toml:"sentinel,omitempty" yaml:"sentinel,omitempty"`
|
||||
}
|
||||
|
||||
// Sentinel holds the Redis Sentinel configuration.
|
||||
type Sentinel struct {
|
||||
MasterName string `description:"Name of the master." json:"masterName,omitempty" toml:"masterName,omitempty" yaml:"masterName,omitempty" export:"true"`
|
||||
Username string `description:"Username for Sentinel authentication." json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty" export:"true"`
|
||||
Password string `description:"Password for Sentinel authentication." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty" export:"true"`
|
||||
|
||||
LatencyStrategy bool `description:"Defines whether to route commands to the closest master or replica nodes (mutually exclusive with RandomStrategy and ReplicaStrategy)." json:"latencyStrategy,omitempty" toml:"latencyStrategy,omitempty" yaml:"latencyStrategy,omitempty" export:"true"`
|
||||
RandomStrategy bool `description:"Defines whether to route commands randomly to master or replica nodes (mutually exclusive with LatencyStrategy and ReplicaStrategy)." json:"randomStrategy,omitempty" toml:"randomStrategy,omitempty" yaml:"randomStrategy,omitempty" export:"true"`
|
||||
ReplicaStrategy bool `description:"Defines whether to route all commands to replica nodes (mutually exclusive with LatencyStrategy and RandomStrategy)." json:"replicaStrategy,omitempty" toml:"replicaStrategy,omitempty" yaml:"replicaStrategy,omitempty" export:"true"`
|
||||
|
||||
UseDisconnectedReplicas bool `description:"Use replicas disconnected with master when cannot get connected replicas." json:"useDisconnectedReplicas,omitempty" toml:"useDisconnectedReplicas,omitempty" yaml:"useDisconnectedReplicas,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
@ -44,5 +59,26 @@ func (p *Provider) Init() error {
|
||||
}
|
||||
}
|
||||
|
||||
if p.Sentinel != nil {
|
||||
switch {
|
||||
case p.Sentinel.LatencyStrategy && !(p.Sentinel.RandomStrategy || p.Sentinel.ReplicaStrategy):
|
||||
case p.Sentinel.RandomStrategy && !(p.Sentinel.LatencyStrategy || p.Sentinel.ReplicaStrategy):
|
||||
case p.Sentinel.ReplicaStrategy && !(p.Sentinel.RandomStrategy || p.Sentinel.LatencyStrategy):
|
||||
return errors.New("latencyStrategy, randomStrategy and replicaStrategy options are mutually exclusive, please use only one of those options")
|
||||
}
|
||||
|
||||
clusterClient := p.Sentinel.LatencyStrategy || p.Sentinel.RandomStrategy
|
||||
config.Sentinel = &redis.Sentinel{
|
||||
MasterName: p.Sentinel.MasterName,
|
||||
Username: p.Sentinel.Username,
|
||||
Password: p.Sentinel.Password,
|
||||
ClusterClient: clusterClient,
|
||||
RouteByLatency: p.Sentinel.LatencyStrategy,
|
||||
RouteRandomly: p.Sentinel.RandomStrategy,
|
||||
ReplicaOnly: p.Sentinel.ReplicaStrategy,
|
||||
UseDisconnectedReplicas: p.Sentinel.UseDisconnectedReplicas,
|
||||
}
|
||||
}
|
||||
|
||||
return p.Provider.Init(redis.StoreName, "redis", config)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user