mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-05-19 17:01:48 +08:00
feat(home): add support for disabling cluster discovery in Redis configuration
This commit is contained in:
@@ -76,10 +76,11 @@ func parseHomeURLConfig(rawAddr string, password string) (config.HomeConfig, err
|
||||
Port: port,
|
||||
Password: password,
|
||||
}
|
||||
query := parsed.Query()
|
||||
homeCfg.DisableClusterDiscovery = parseHomeBoolQuery(query, "disable-cluster-discovery", "disable_cluster_discovery")
|
||||
|
||||
if scheme == "rediss" {
|
||||
homeCfg.TLS.Enable = true
|
||||
query := parsed.Query()
|
||||
homeCfg.TLS.ServerName = strings.TrimSpace(firstHomeQueryValue(query, "server-name", "server_name"))
|
||||
homeCfg.TLS.InsecureSkipVerify = parseHomeBoolQuery(query, "insecure-skip-verify", "insecure_skip_verify", "skip_verify")
|
||||
homeCfg.TLS.CACert = strings.TrimSpace(firstHomeQueryValue(query, "ca-cert", "ca_cert"))
|
||||
|
||||
@@ -64,3 +64,14 @@ func TestParseHomeFlagConfigPasswordFlagOverridesURLPassword(t *testing.T) {
|
||||
t.Fatalf("Password = %q, want flag-secret", cfg.Password)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHomeFlagConfigDisableClusterDiscovery(t *testing.T) {
|
||||
cfg, err := parseHomeFlagConfig("redis://home.example.com:8327?disable-cluster-discovery=true", "")
|
||||
if err != nil {
|
||||
t.Fatalf("parseHomeFlagConfig() error = %v", err)
|
||||
}
|
||||
|
||||
if !cfg.DisableClusterDiscovery {
|
||||
t.Fatal("DisableClusterDiscovery = false, want true")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ func main() {
|
||||
var password string
|
||||
var homeAddr string
|
||||
var homePassword string
|
||||
var homeDisableClusterDiscovery bool
|
||||
var tuiMode bool
|
||||
var standalone bool
|
||||
var localModel bool
|
||||
@@ -93,6 +94,7 @@ func main() {
|
||||
flag.StringVar(&password, "password", "", "")
|
||||
flag.StringVar(&homeAddr, "home", "", "Home control plane address in host:port, redis://host:port, or rediss://host:port format (loads config from home and skips local config file)")
|
||||
flag.StringVar(&homePassword, "home-password", "", "Home control plane password (Redis AUTH)")
|
||||
flag.BoolVar(&homeDisableClusterDiscovery, "home-disable-cluster-discovery", false, "Disable Home CLUSTER NODES discovery and keep using the configured -home address")
|
||||
flag.BoolVar(&tuiMode, "tui", false, "Start with terminal management UI")
|
||||
flag.BoolVar(&standalone, "standalone", false, "In TUI mode, start an embedded local server")
|
||||
flag.BoolVar(&localModel, "local-model", false, "Use embedded model catalog only, skip remote model fetching")
|
||||
@@ -250,6 +252,9 @@ func main() {
|
||||
log.Errorf("invalid -home address %q: %v", homeAddr, errHomeCfg)
|
||||
return
|
||||
}
|
||||
if homeDisableClusterDiscovery {
|
||||
homeCfg.DisableClusterDiscovery = true
|
||||
}
|
||||
homeClient := home.New(homeCfg)
|
||||
defer homeClient.Close()
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@ home:
|
||||
host: "127.0.0.1"
|
||||
port: 6379
|
||||
password: ""
|
||||
# Keep CPA pinned to the configured home address instead of switching to CLUSTER NODES entries.
|
||||
# Useful when Home is behind NAT, Docker networking, or a reverse proxy.
|
||||
disable-cluster-discovery: false
|
||||
# Optional TLS for the outbound Redis connection to the home control plane.
|
||||
# Enable this when connecting through rediss:// or an SSL stream proxy.
|
||||
tls:
|
||||
|
||||
@@ -2,11 +2,12 @@ package config
|
||||
|
||||
// HomeConfig configures the optional "home" control plane integration over Redis protocol.
|
||||
type HomeConfig struct {
|
||||
Enabled bool `yaml:"enabled" json:"enabled"`
|
||||
Host string `yaml:"host" json:"-"`
|
||||
Port int `yaml:"port" json:"-"`
|
||||
Password string `yaml:"password" json:"-"`
|
||||
TLS HomeTLSConfig `yaml:"tls" json:"-"`
|
||||
Enabled bool `yaml:"enabled" json:"enabled"`
|
||||
Host string `yaml:"host" json:"-"`
|
||||
Port int `yaml:"port" json:"-"`
|
||||
Password string `yaml:"password" json:"-"`
|
||||
DisableClusterDiscovery bool `yaml:"disable-cluster-discovery" json:"-"`
|
||||
TLS HomeTLSConfig `yaml:"tls" json:"-"`
|
||||
}
|
||||
|
||||
// HomeTLSConfig configures client-side TLS for the home Redis connection.
|
||||
|
||||
@@ -9,6 +9,7 @@ home:
|
||||
host: home.example.com
|
||||
port: 444
|
||||
password: secret
|
||||
disable-cluster-discovery: true
|
||||
tls:
|
||||
enable: true
|
||||
server-name: home.example.com
|
||||
@@ -31,6 +32,9 @@ home:
|
||||
if cfg.Home.Password != "secret" {
|
||||
t.Fatalf("Home.Password = %q, want secret", cfg.Home.Password)
|
||||
}
|
||||
if !cfg.Home.DisableClusterDiscovery {
|
||||
t.Fatal("Home.DisableClusterDiscovery = false, want true")
|
||||
}
|
||||
if !cfg.Home.TLS.Enable {
|
||||
t.Fatal("Home.TLS.Enable = false, want true")
|
||||
}
|
||||
|
||||
@@ -265,7 +265,23 @@ func (c *Client) Ping(ctx context.Context) error {
|
||||
return cmd.Ping(ctx).Err()
|
||||
}
|
||||
|
||||
func (c *Client) clusterDiscoveryEnabled() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
return c.clusterDiscoveryEnabledLocked()
|
||||
}
|
||||
|
||||
func (c *Client) clusterDiscoveryEnabledLocked() bool {
|
||||
return !c.homeCfg.DisableClusterDiscovery
|
||||
}
|
||||
|
||||
func (c *Client) refreshBestClusterNode(ctx context.Context) {
|
||||
if !c.clusterDiscoveryEnabled() {
|
||||
return
|
||||
}
|
||||
switched, errRefresh := c.refreshClusterNodes(ctx)
|
||||
if errRefresh != nil {
|
||||
log.Debugf("home cluster nodes unavailable: %v", errRefresh)
|
||||
@@ -279,6 +295,9 @@ func (c *Client) refreshBestClusterNode(ctx context.Context) {
|
||||
}
|
||||
|
||||
func (c *Client) refreshClusterNodes(ctx context.Context) (bool, error) {
|
||||
if !c.clusterDiscoveryEnabled() {
|
||||
return false, nil
|
||||
}
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
@@ -353,6 +372,10 @@ func (c *Client) failoverAfterReconnectFailure() (bool, string) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if !c.clusterDiscoveryEnabledLocked() {
|
||||
c.reconnectFailures = 0
|
||||
return false, ""
|
||||
}
|
||||
c.reconnectFailures++
|
||||
if c.reconnectFailures < homeReconnectFailoverThreshold {
|
||||
return false, ""
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package home
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
@@ -115,3 +116,44 @@ func TestRedisOptionsHomeTLSEnabledUsesExplicitServerName(t *testing.T) {
|
||||
t.Fatal("InsecureSkipVerify = false, want true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefreshClusterNodesDisabledSkipsRedisCommand(t *testing.T) {
|
||||
client := New(config.HomeConfig{
|
||||
Enabled: true,
|
||||
Host: "127.0.0.1",
|
||||
Port: 1,
|
||||
DisableClusterDiscovery: true,
|
||||
})
|
||||
|
||||
switched, err := client.refreshClusterNodes(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("refreshClusterNodes() error = %v", err)
|
||||
}
|
||||
if switched {
|
||||
t.Fatal("refreshClusterNodes() switched = true, want false")
|
||||
}
|
||||
if client.cmd != nil || client.sub != nil {
|
||||
t.Fatalf("redis clients were initialized when cluster discovery was disabled")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFailoverAfterReconnectFailureDisabledDoesNotSwitchToClusterNode(t *testing.T) {
|
||||
client := New(config.HomeConfig{
|
||||
Enabled: true,
|
||||
Host: "seed.example.com",
|
||||
Port: 8327,
|
||||
DisableClusterDiscovery: true,
|
||||
})
|
||||
client.mu.Lock()
|
||||
client.clusterNodes = []clusterNode{{IP: "other.example.com", Port: 8327}}
|
||||
client.reconnectFailures = homeReconnectFailoverThreshold - 1
|
||||
client.mu.Unlock()
|
||||
|
||||
switched, addr := client.failoverAfterReconnectFailure()
|
||||
if switched {
|
||||
t.Fatalf("failoverAfterReconnectFailure() switched to %s, want no switch", addr)
|
||||
}
|
||||
if got, _ := client.addr(); got != "seed.example.com:8327" {
|
||||
t.Fatalf("addr() = %q, want seed.example.com:8327", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user