2024-01-09 19:00:07 +03:00
package integration
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"testing"
"text/template"
"time"
"github.com/fatih/structs"
"github.com/kvtools/redis"
"github.com/kvtools/valkeyrie"
"github.com/kvtools/valkeyrie/store"
"github.com/pmezard/go-difflib/difflib"
2024-01-10 12:47:44 +03:00
"github.com/rs/zerolog/log"
2024-01-09 19:00:07 +03:00
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
2024-01-10 12:47:44 +03:00
"github.com/traefik/traefik/v3/integration/try"
"github.com/traefik/traefik/v3/pkg/api"
2024-01-09 19:00:07 +03:00
)
// Redis test suites.
type RedisSentinelSuite struct {
BaseSuite
kvClient store . Store
redisEndpoints [ ] string
}
func TestRedisSentinelSuite ( t * testing . T ) {
suite . Run ( t , new ( RedisSentinelSuite ) )
}
func ( s * RedisSentinelSuite ) SetupSuite ( ) {
s . BaseSuite . SetupSuite ( )
s . setupSentinelConfiguration ( [ ] string { "26379" , "26379" , "26379" } )
s . createComposeProject ( "redis_sentinel" )
s . composeUp ( )
s . redisEndpoints = [ ] string {
net . JoinHostPort ( s . getComposeServiceIP ( "sentinel1" ) , "26379" ) ,
net . JoinHostPort ( s . getComposeServiceIP ( "sentinel2" ) , "26379" ) ,
net . JoinHostPort ( s . getComposeServiceIP ( "sentinel3" ) , "26379" ) ,
}
kv , err := valkeyrie . NewStore (
context . Background ( ) ,
redis . StoreName ,
s . redisEndpoints ,
& redis . Config {
Sentinel : & redis . Sentinel {
MasterName : "mymaster" ,
} ,
} ,
)
require . NoError ( s . T ( ) , err , "Cannot create store redis" )
s . kvClient = kv
// wait for redis
err = try . Do ( 60 * time . Second , try . KVExists ( kv , "test" ) )
require . NoError ( s . T ( ) , err )
}
func ( s * RedisSentinelSuite ) TearDownSuite ( ) {
s . BaseSuite . TearDownSuite ( )
for _ , filename := range [ ] string { "sentinel1.conf" , "sentinel2.conf" , "sentinel3.conf" } {
_ = os . Remove ( filepath . Join ( "." , "resources" , "compose" , "config" , filename ) )
}
}
func ( s * RedisSentinelSuite ) setupSentinelConfiguration ( 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 )
require . NoError ( s . T ( ) , err )
folder , prefix := filepath . Split ( templateFile )
fileName := fmt . Sprintf ( "%s/sentinel%d.conf" , folder , i + 1 )
tmpFile , err := os . Create ( fileName )
require . NoError ( s . T ( ) , err )
defer tmpFile . Close ( )
model := structs . Map ( templateValue )
model [ "SelfFilename" ] = tmpFile . Name ( )
err = tmpl . ExecuteTemplate ( tmpFile , prefix , model )
require . NoError ( s . T ( ) , err )
err = tmpFile . Sync ( )
require . NoError ( s . T ( ) , err )
}
}
func ( s * RedisSentinelSuite ) TestSentinelConfiguration ( ) {
file := s . adaptFile ( "fixtures/redis/sentinel.toml" , struct { RedisAddress string } {
RedisAddress : strings . Join ( s . redisEndpoints , ` "," ` ) ,
} )
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" ,
}
for k , v := range data {
err := s . kvClient . Put ( context . Background ( ) , k , [ ] byte ( v ) , nil )
require . NoError ( s . T ( ) , err )
}
s . traefikCmd ( withConfigFile ( file ) )
// 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": ` ) ,
)
require . NoError ( s . T ( ) , err )
resp , err := http . Get ( "http://127.0.0.1:8080/api/rawdata" )
require . NoError ( s . T ( ) , err )
var obtained api . RunTimeRepresentation
err = json . NewDecoder ( resp . Body ) . Decode ( & obtained )
require . NoError ( s . T ( ) , err )
got , err := json . MarshalIndent ( obtained , "" , " " )
require . NoError ( s . T ( ) , err )
expectedJSON := filepath . FromSlash ( "testdata/rawdata-redis.json" )
if * updateExpected {
err = os . WriteFile ( expectedJSON , got , 0 o666 )
require . NoError ( s . T ( ) , err )
}
expected , err := os . ReadFile ( expectedJSON )
require . NoError ( s . T ( ) , err )
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 )
require . NoError ( s . T ( ) , err )
2024-01-10 12:47:44 +03:00
log . Info ( ) . Msg ( text )
2024-01-09 19:00:07 +03:00
}
}