futriix/tests/unit/cluster/announce-client-ip.tcl
Viktor Söderqvist a323dce890
Dual stack and client-specific IPs in cluster (#736)
New configs:

* `cluster-announce-client-ipv4`
* `cluster-announce-client-ipv6`

New module API function:

* `ValkeyModule_GetClusterNodeInfoForClient`, takes a client id and is
otherwise just like its non-ForClient cousin.

If configured, one of these IP addresses are reported to each client in
CLUSTER SLOTS, CLUSTER SHARDS, CLUSTER NODES and redirects, replacing
the IP (`custer-announce-ip` or the auto-detected IP) of each node.
Which one is reported to the client depends on whether the client is
connected over IPv4 or IPv6.

Benefits:

* This allows clients using IPv4 to get the IPv4 addresses of all
cluster nodes and IPv6 clients to get the IPv6 clients.
* This allows the IPs visible to clients to be different to the IPs used
between the cluster nodes due to NAT'ing.

The information is propagated in the cluster bus using new Ping
extensions. (Old nodes without this feature ignore unknown Ping
extensions.)

This adds another dimension to CLUSTER SLOTS reply. It now depends on
the client's use of TLS, the IP address family and RESP version.
Refactoring: The cached connection type definition is moved from
connection.h (it actually has nothing to do with the connection
abstraction) to server.h and is changed to a bitmap, with one bit for
each of TLS, IPv6 and RESP3.

Fixes #337

---------

Signed-off-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
2024-07-10 13:53:52 +02:00

150 lines
5.1 KiB
Tcl

# Small cluster. No need for failovers.
start_cluster 2 2 {tags {external:skip cluster} overrides {cluster-replica-no-failover yes}} {
test "Set cluster announced IPv4 to invalid IP" {
catch {R 0 config set cluster-announce-client-ipv4 banana} e
assert_match "*Invalid IPv4 address*" $e
}
test "Set cluster announced IPv4 and check that it propagates" {
for {set j 0} {$j < [llength $::servers]} {incr j} {
set res [R $j config set cluster-announce-client-ipv4 "111.222.111.$j"]
}
# CLUSTER SLOTS
wait_for_condition 50 100 {
[are_cluster_announced_ips_propagated {111.222.111.*}]
} else {
fail "cluster-announce-client-ipv4 were not propagated"
}
# CLUSTER SHARDS
for {set j 0} {$j < [llength $::servers]} {incr j} {
foreach shard [R $j CLUSTER SHARDS] {
foreach node [dict get $shard "nodes"] {
set ip [dict get $node "ip"]
set endpoint [dict get $node "endpoint"]
assert_match "111.222.111*" $ip
assert_match "111.222.111*" $endpoint
}
}
}
# CLUSTER NODES
for {set j 0} {$j < [llength $::servers]} {incr j} {
set lines [split [R $j CLUSTER NODES] "\r\n"]
foreach l $lines {
set l [string trim $l]
if {$l eq {}} continue
assert_equal 1 [regexp {^[0-9a-f]+ 111\.222\.111\.[0-9]} $l]
}
}
# Redirects
catch {R 0 set foo foo} e
assert_match "MOVED * 111.222.111*:*" $e
# Now that everything is propagated, assert everyone agrees
wait_for_cluster_propagation
}
test "Clear announced client IPv4 and check that it propagates" {
for {set j 0} {$j < [llength $::servers]} {incr j} {
R $j config set cluster-announce-client-ipv4 ""
}
wait_for_condition 50 100 {
[are_cluster_announced_ips_propagated "127.0.0.1"] eq 1
} else {
fail "Cleared cluster-announce-client-ipv4 were not propagated"
}
# Redirect use the IP address
catch {R 0 set foo foo} e
assert_match "MOVED * 127.0.0.1:*" $e
# Now that everything is propagated, assert everyone agrees
wait_for_cluster_propagation
}
}
start_cluster 2 2 {tags {external:skip cluster ipv6} overrides {cluster-replica-no-failover yes bind {127.0.0.1 ::1}}} {
# Connecting to localhost as "::1" makes the clients use IPv6.
set clients {}
for {set j 0} {$j < [llength $::servers]} {incr j} {
set level [expr -1 * $j]
lappend clients [valkey ::1 [srv $level port] 0 $::tls]
}
test "Set cluster announced IPv6 to invalid IP" {
catch {R 0 config set cluster-announce-client-ipv6 banana} e
assert_match "*Invalid IPv6 address*" $e
}
test "Set cluster announced IPv6 and check that it propagates" {
for {set j 0} {$j < [llength $::servers]} {incr j} {
R $j config set cluster-announce-client-ipv6 "cafe:1234::$j"
}
# CLUSTER SLOTS
wait_for_condition 50 100 {
[are_cluster_announced_ips_propagated "cafe:1234::*" $clients] eq 1
} else {
fail "cluster-announce-client-ipv6 were not propagated"
}
# CLUSTER SHARDS
for {set j 0} {$j < [llength $::servers]} {incr j} {
foreach shard [[lindex $clients $j] CLUSTER SHARDS] {
foreach node [dict get $shard "nodes"] {
set ip [dict get $node "ip"]
set endpoint [dict get $node "endpoint"]
assert_match "cafe:1234::*" $ip
assert_match "cafe:1234::*" $endpoint
}
}
}
# CLUSTER NODES
for {set j 0} {$j < [llength $::servers]} {incr j} {
set lines [split [[lindex $clients $j] CLUSTER NODES] "\r\n"]
foreach l $lines {
set l [string trim $l]
if {$l eq {}} continue
assert_equal 1 [regexp {^[0-9a-f]+ cafe:1234::[0-9]} $l]
}
}
# Redirects
catch {[lindex $clients 0] set foo foo} e
assert_match "MOVED * cafe:1234::*:*" $e
# Now that everything is propagated, assert everyone agrees
wait_for_cluster_propagation
}
test "Clear announced client IPv6 and check that it propagates" {
for {set j 0} {$j < [llength $::servers]} {incr j} {
R $j config set cluster-announce-client-ipv6 ""
}
wait_for_condition 50 100 {
[are_cluster_announced_ips_propagated "127.0.0.1" $clients] eq 1
} else {
fail "Cleared cluster-announce-client-ipv6 were not propagated"
}
# Redirects
catch {[lindex $clients 0] set foo foo} e
assert_match "MOVED * 127.0.0.1:*" $e
# Now that everything is propagated, assert everyone agrees
wait_for_cluster_propagation
}
# Close clients
for {set j 0} {$j < [llength $::servers]} {incr j} {
[lindex $clients $j] close
}
}