Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.26
require (
dario.cat/mergo v1.0.2
github.com/1password/onepassword-sdk-go v0.3.1
github.com/alicebob/miniredis/v2 v2.37.0
github.com/alicebob/miniredis/v2 v2.38.0
github.com/atotto/clipboard v0.1.4
github.com/aws/aws-sdk-go-v2 v1.41.7
github.com/aws/aws-sdk-go-v2/config v1.32.17
Expand Down Expand Up @@ -45,11 +45,11 @@ require (
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/pressly/goose/v3 v3.27.0
github.com/prometheus/client_golang v1.23.2
github.com/redis/go-redis/v9 v9.18.0
github.com/redis/go-redis/v9 v9.19.0
github.com/shirou/gopsutil/v4 v4.26.3
github.com/spf13/viper v1.21.0
github.com/stacklok/toolhive-catalog v0.20260518.0
github.com/stacklok/toolhive-core v0.0.20
github.com/stacklok/toolhive-core v0.0.21
github.com/stretchr/testify v1.11.1
github.com/swaggo/swag/v2 v2.0.0-rc5
github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd
Expand Down Expand Up @@ -133,7 +133,6 @@ require (
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
github.com/danieljoos/wincred v1.2.3 // indirect
github.com/dgraph-io/ristretto v1.0.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect
github.com/docker/cli v29.4.0+incompatible // indirect
Expand Down
18 changes: 8 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNx
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
github.com/alicebob/miniredis/v2 v2.37.0 h1:RheObYW32G1aiJIj81XVt78ZHJpHonHLHW7OLIshq68=
github.com/alicebob/miniredis/v2 v2.37.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
github.com/alicebob/miniredis/v2 v2.38.0 h1:nZAzCR+Lj+Vxk4ZXzm2NuKq2O33RXj1XxJ2e2uP9jiw=
github.com/alicebob/miniredis/v2 v2.38.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
Expand Down Expand Up @@ -178,8 +178,6 @@ github.com/dgraph-io/ristretto v1.0.0 h1:SYG07bONKMlFDUYu5pEu3DGAh8c2OFNzKm6G9J4
github.com/dgraph-io/ristretto v1.0.0/go.mod h1:jTi2FiYEhQ1NsMmA7DeBykizjOuY88NhKBkepyu1jPc=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/digitorus/pkcs7 v0.0.0-20230713084857-e76b763bdc49/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc=
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 h1:ge14PCmCvPjpMQMIAH7uKg0lrtNSOdpYsRXlwk3QbaE=
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc=
Expand Down Expand Up @@ -717,8 +715,8 @@ github.com/prometheus/otlptranslator v1.0.0 h1:s0LJW/iN9dkIH+EnhiD3BlkkP5QVIUVEo
github.com/prometheus/otlptranslator v1.0.0/go.mod h1:vRYWnXvI6aWGpsdY/mOT/cbeVRBlPWtBNDb7kGR3uKM=
github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
github.com/redis/go-redis/v9 v9.18.0 h1:pMkxYPkEbMPwRdenAzUNyFNrDgHx9U+DrBabWNfSRQs=
github.com/redis/go-redis/v9 v9.18.0/go.mod h1:k3ufPphLU5YXwNTUcCRXGxUoF1fqxnhFQmscfkCoDA0=
github.com/redis/go-redis/v9 v9.19.0 h1:XPVaaPSnG6RhYf7p+rmSa9zZfeVAnWsH5h3lxthOm/k=
github.com/redis/go-redis/v9 v9.19.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
Expand Down Expand Up @@ -807,8 +805,8 @@ github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/stacklok/toolhive-catalog v0.20260518.0 h1:J4Adoaedsm09RB/8Dx2PSlGgZ4BscYOgmUrWzMIcMY4=
github.com/stacklok/toolhive-catalog v0.20260518.0/go.mod h1:e/zMn+cmBvXMGtRJu0WYO71h0UVRgXTq32849PkQgss=
github.com/stacklok/toolhive-core v0.0.20 h1:7XZTN6XAd8yHjHaLxtiTXOocVyBrKoRh7Ygw417/8Z0=
github.com/stacklok/toolhive-core v0.0.20/go.mod h1:ZEAdmyyHdxikO7CvpYY/xLctPxh1ZW3+ZneMMIbbZHw=
github.com/stacklok/toolhive-core v0.0.21 h1:UxCDSmhw0k4QP6gr0F1CLfPuI3bVHBu3VuHt/DLYH50=
github.com/stacklok/toolhive-core v0.0.21/go.mod h1:59Fo8iGJEEGJB6i/N/4fiB2Z+7AhBbPrguiggtED5TU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
Expand Down Expand Up @@ -902,8 +900,8 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/zalando/go-keyring v0.2.8 h1:6sD/Ucpl7jNq10rM2pgqTs0sZ9V3qMrqfIIy5YPccHs=
github.com/zalando/go-keyring v0.2.8/go.mod h1:tsMo+VpRq5NGyKfxoBVjCuMrG47yj8cmakZDO5QGii0=
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
Expand Down
81 changes: 40 additions & 41 deletions pkg/authserver/runner/embeddedauthserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"sync"
"time"

tcredis "github.com/stacklok/toolhive-core/redis"
"github.com/stacklok/toolhive/pkg/auth/dcr"
"github.com/stacklok/toolhive/pkg/authserver"
servercrypto "github.com/stacklok/toolhive/pkg/authserver/server/crypto"
Expand Down Expand Up @@ -643,100 +644,97 @@ func createStorage(ctx context.Context, cfg *storage.RunConfig) (storage.Storage
if err != nil {
return nil, fmt.Errorf("invalid Redis config: %w", err)
}
return storage.NewRedisStorage(ctx, *redisCfg)
return storage.NewRedisStorage(ctx, redisCfg, cfg.RedisConfig.KeyPrefix)
}
return nil, fmt.Errorf("unsupported storage type: %s", cfg.Type)
}

// convertRedisRunConfig converts a serializable RedisRunConfig to the runtime RedisConfig.
// It resolves credentials from environment variables and parses duration strings.
func convertRedisRunConfig(rc *storage.RedisRunConfig) (*storage.RedisConfig, error) {
// convertRedisRunConfig converts a serializable RedisRunConfig to a runtime
// tcredis.Config. It resolves ACL credentials from environment variables and
// parses duration strings. Connection-mode topology and defaulting are handled
// by the shared toolhive-core redis package when the client is constructed.
func convertRedisRunConfig(rc *storage.RedisRunConfig) (tcredis.Config, error) {
if rc == nil {
return nil, fmt.Errorf("redis config is required when storage type is redis")
return tcredis.Config{}, fmt.Errorf("redis config is required when storage type is redis")
}

if rc.Addr != "" && rc.SentinelConfig != nil {
return nil, fmt.Errorf("addr and sentinel_config are mutually exclusive; exactly one must be set")
}
if rc.Addr == "" && rc.SentinelConfig == nil {
return nil, fmt.Errorf("one of addr (standalone or cluster) or sentinel_config (sentinel) is required")
}
if rc.ClusterMode && rc.SentinelConfig != nil {
return nil, fmt.Errorf("cluster mode cannot be used with sentinel configuration")
}

cfg := &storage.RedisConfig{
cfg := tcredis.Config{
Addr: rc.Addr,
ClusterMode: rc.ClusterMode,
KeyPrefix: rc.KeyPrefix,
}

if rc.SentinelConfig != nil {
cfg.SentinelConfig = &storage.SentinelConfig{
cfg.SentinelConfig = &tcredis.SentinelConfig{
MasterName: rc.SentinelConfig.MasterName,
SentinelAddrs: rc.SentinelConfig.SentinelAddrs,
DB: rc.SentinelConfig.DB,
}
cfg.DB = rc.SentinelConfig.DB
}

aclCfg, err := convertRedisACLConfig(rc.ACLUserConfig)
acl, err := convertRedisACLConfig(rc.ACLUserConfig)
if err != nil {
return nil, fmt.Errorf("failed to convert ACL config: %w", err)
return tcredis.Config{}, fmt.Errorf("failed to convert ACL config: %w", err)
}
cfg.ACLUserConfig = aclCfg
cfg.Username = acl.username
cfg.Password = acl.password

if err := applyRedisTimeouts(rc, cfg); err != nil {
return nil, fmt.Errorf("failed to apply redis timeouts: %w", err)
if err := applyRedisTimeouts(rc, &cfg); err != nil {
return tcredis.Config{}, fmt.Errorf("failed to apply redis timeouts: %w", err)
}

tlsCfg, err := convertRedisTLSRunConfig(rc.TLS)
if err != nil {
return nil, fmt.Errorf("master TLS config: %w", err)
return tcredis.Config{}, fmt.Errorf("master TLS config: %w", err)
}
cfg.TLS = tlsCfg

// SentinelTLS only applies in Sentinel mode
if rc.SentinelConfig != nil {
sentinelTLSCfg, err := convertRedisTLSRunConfig(rc.SentinelTLS)
if err != nil {
return nil, fmt.Errorf("sentinel TLS config: %w", err)
return tcredis.Config{}, fmt.Errorf("sentinel TLS config: %w", err)
}
cfg.SentinelTLS = sentinelTLSCfg
}

return cfg, nil
}

// redisACLCredentials carries resolved Redis ACL credentials between
// convertRedisACLConfig and its caller. Named fields prevent positional
// swaps of two same-typed strings at the call site.
type redisACLCredentials struct {
username string
password string
}

// convertRedisACLConfig resolves ACL user credentials from environment variables.
// When UsernameEnvVar is empty, no username is resolved; go-redis then sends
// HELLO with "default" as the username (or falls back to legacy AUTH <password>
// for servers that do not support HELLO). This is required for managed Redis
// tiers without ACL users (e.g. GCP Memorystore Basic/Standard HA, Azure Cache
// for Redis).
func convertRedisACLConfig(rc *storage.ACLUserRunConfig) (*storage.ACLUserConfig, error) {
func convertRedisACLConfig(rc *storage.ACLUserRunConfig) (redisACLCredentials, error) {
if rc == nil {
return nil, fmt.Errorf("acl user config is required")
return redisACLCredentials{}, fmt.Errorf("acl user config is required")
}
var username string
if rc.UsernameEnvVar != "" {
var err error
username, err = resolveEnvVar(rc.UsernameEnvVar)
if err != nil {
return nil, fmt.Errorf("failed to resolve Redis username: %w", err)
return redisACLCredentials{}, fmt.Errorf("failed to resolve Redis username: %w", err)
}
}
password, err := resolveEnvVar(rc.PasswordEnvVar)
if err != nil {
return nil, fmt.Errorf("failed to resolve Redis password: %w", err)
return redisACLCredentials{}, fmt.Errorf("failed to resolve Redis password: %w", err)
}
return &storage.ACLUserConfig{
Username: username,
Password: password,
}, nil
return redisACLCredentials{username: username, password: password}, nil
}

// applyRedisTimeouts parses and applies optional timeout duration strings to cfg.
func applyRedisTimeouts(rc *storage.RedisRunConfig, cfg *storage.RedisConfig) error {
func applyRedisTimeouts(rc *storage.RedisRunConfig, cfg *tcredis.Config) error {
if rc.DialTimeout != "" {
d, err := time.ParseDuration(rc.DialTimeout)
if err != nil {
Expand All @@ -761,15 +759,16 @@ func applyRedisTimeouts(rc *storage.RedisRunConfig, cfg *storage.RedisConfig) er
return nil
}

// convertRedisTLSRunConfig converts a RedisTLSRunConfig to runtime RedisTLSConfig.
// Returns an error if a CA cert file is configured but cannot be read — this is
// treated as a hard error because silently falling back to system CAs could mask
// a misconfiguration and cause confusing TLS failures downstream.
func convertRedisTLSRunConfig(rc *storage.RedisTLSRunConfig) (*storage.RedisTLSConfig, error) {
// convertRedisTLSRunConfig converts a RedisTLSRunConfig to a runtime
// tcredis.TLSConfig. Returns an error if a CA cert file is configured but
// cannot be read — this is treated as a hard error because silently falling
// back to system CAs could mask a misconfiguration and cause confusing TLS
// failures downstream.
func convertRedisTLSRunConfig(rc *storage.RedisTLSRunConfig) (*tcredis.TLSConfig, error) {
if rc == nil {
return nil, nil
}
cfg := &storage.RedisTLSConfig{
cfg := &tcredis.TLSConfig{
InsecureSkipVerify: rc.InsecureSkipVerify,
}
if rc.CACertFile != "" {
Expand Down
101 changes: 10 additions & 91 deletions pkg/authserver/runner/embeddedauthserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1166,24 +1166,11 @@ func TestCreateStorage(t *testing.T) {
assert.Contains(t, err.Error(), "redis config is required")
})

t.Run("redis type with missing sentinel config returns error", func(t *testing.T) {
t.Parallel()

_, err := createStorage(ctx, &storage.RunConfig{
Type: string(storage.TypeRedis),
RedisConfig: &storage.RedisRunConfig{
KeyPrefix: "test:",
ACLUserConfig: &storage.ACLUserRunConfig{
UsernameEnvVar: "REDIS_USER",
PasswordEnvVar: "REDIS_PASS",
},
},
})
require.Error(t, err)
assert.Contains(t, err.Error(), "one of addr (standalone or cluster) or sentinel_config (sentinel) is required")
})
}

// TestConvertRedisRunConfig covers the runner-owned conversion steps: nil
// guard and ACL credential resolution. Connection-mode topology validation is
// owned by the shared toolhive-core redis package and exercised in its tests.
func TestConvertRedisRunConfig(t *testing.T) {
t.Parallel()

Expand All @@ -1194,19 +1181,6 @@ func TestConvertRedisRunConfig(t *testing.T) {
assert.Contains(t, err.Error(), "redis config is required")
})

t.Run("missing sentinel config returns error", func(t *testing.T) {
t.Parallel()
_, err := convertRedisRunConfig(&storage.RedisRunConfig{
KeyPrefix: "test:",
ACLUserConfig: &storage.ACLUserRunConfig{
UsernameEnvVar: "USER",
PasswordEnvVar: "PASS",
},
})
require.Error(t, err)
assert.Contains(t, err.Error(), "one of addr (standalone or cluster) or sentinel_config (sentinel) is required")
})

t.Run("missing ACL user config returns error", func(t *testing.T) {
t.Parallel()
_, err := convertRedisRunConfig(&storage.RedisRunConfig{
Expand Down Expand Up @@ -1236,54 +1210,6 @@ func TestConvertRedisRunConfig(t *testing.T) {
require.Error(t, err)
assert.Contains(t, err.Error(), "failed to resolve Redis username")
})

t.Run("addr and sentinel config both set returns error", func(t *testing.T) {
t.Parallel()
_, err := convertRedisRunConfig(&storage.RedisRunConfig{
Addr: "redis.example.com:6379",
SentinelConfig: &storage.SentinelRunConfig{
MasterName: "mymaster",
SentinelAddrs: []string{"sentinel:26379"},
},
ACLUserConfig: &storage.ACLUserRunConfig{
UsernameEnvVar: "USER",
PasswordEnvVar: "PASS",
},
KeyPrefix: "thv:",
})
require.Error(t, err)
assert.Contains(t, err.Error(), "mutually exclusive")
})

t.Run("neither addr nor sentinel config returns error", func(t *testing.T) {
t.Parallel()
_, err := convertRedisRunConfig(&storage.RedisRunConfig{
ACLUserConfig: &storage.ACLUserRunConfig{
UsernameEnvVar: "USER",
PasswordEnvVar: "PASS",
},
KeyPrefix: "thv:",
})
require.Error(t, err)
assert.Contains(t, err.Error(), "one of addr")
})

t.Run("cluster mode with sentinel also set returns error", func(t *testing.T) {
t.Parallel()
_, err := convertRedisRunConfig(&storage.RedisRunConfig{
ClusterMode: true,
SentinelConfig: &storage.SentinelRunConfig{
MasterName: "mymaster",
SentinelAddrs: []string{"sentinel:26379"},
},
ACLUserConfig: &storage.ACLUserRunConfig{
PasswordEnvVar: "PASS",
},
KeyPrefix: "thv:",
})
require.Error(t, err)
assert.Contains(t, err.Error(), "cluster mode cannot be used with sentinel")
})
}

// TestConvertRedisRunConfig_WithEnvVars tests convertRedisRunConfig with environment variables.
Expand All @@ -1309,16 +1235,13 @@ func TestConvertRedisRunConfig_WithEnvVars(t *testing.T) {
WriteTimeout: "3s",
})
require.NoError(t, err)
require.NotNil(t, cfg)

assert.Equal(t, "thv:auth:ns:name:", cfg.KeyPrefix)
require.NotNil(t, cfg.SentinelConfig)
assert.Equal(t, "mymaster", cfg.SentinelConfig.MasterName)
assert.Equal(t, []string{"10.0.0.1:26379", "10.0.0.2:26379"}, cfg.SentinelConfig.SentinelAddrs)
assert.Equal(t, 3, cfg.SentinelConfig.DB)
require.NotNil(t, cfg.ACLUserConfig)
assert.Equal(t, "myuser", cfg.ACLUserConfig.Username)
assert.Equal(t, "mypass", cfg.ACLUserConfig.Password)
assert.Equal(t, 3, cfg.DB)
assert.Equal(t, "myuser", cfg.Username)
assert.Equal(t, "mypass", cfg.Password)
assert.Equal(t, 10*time.Second, cfg.DialTimeout)
assert.Equal(t, 5*time.Second, cfg.ReadTimeout)
assert.Equal(t, 3*time.Second, cfg.WriteTimeout)
Expand Down Expand Up @@ -1394,9 +1317,8 @@ func TestConvertRedisRunConfig_WithEnvVars(t *testing.T) {
},
})
require.NoError(t, err)
require.NotNil(t, cfg.ACLUserConfig)
assert.Empty(t, cfg.ACLUserConfig.Username)
assert.Equal(t, "mypass", cfg.ACLUserConfig.Password)
assert.Empty(t, cfg.Username)
assert.Equal(t, "mypass", cfg.Password)
})

t.Run("cluster mode resolves correctly", func(t *testing.T) {
Expand All @@ -1413,14 +1335,11 @@ func TestConvertRedisRunConfig_WithEnvVars(t *testing.T) {
KeyPrefix: "thv:auth:ns:name:",
})
require.NoError(t, err)
require.NotNil(t, cfg)
assert.Equal(t, "discovery.example.com:6379", cfg.Addr)
assert.True(t, cfg.ClusterMode)
assert.Nil(t, cfg.SentinelConfig)
require.NotNil(t, cfg.ACLUserConfig)
assert.Equal(t, "clusteruser", cfg.ACLUserConfig.Username)
assert.Equal(t, "clusterpass", cfg.ACLUserConfig.Password)
assert.Equal(t, "thv:auth:ns:name:", cfg.KeyPrefix)
assert.Equal(t, "clusteruser", cfg.Username)
assert.Equal(t, "clusterpass", cfg.Password)
})
}

Expand Down
Loading
Loading