redict/tests/unit/cluster/hostnames.tcl
Drew DeVault 50ee0f5be8 all: let's go LGPL over GPL
Based on feedback from interested parties
2024-03-21 20:11:44 +01:00

210 lines
8.2 KiB
Tcl

# SPDX-FileCopyrightText: 2024 Redict Contributors
# SPDX-FileCopyrightText: 2024 Salvatore Sanfilippo <antirez at gmail dot com>
#
# SPDX-License-Identifier: BSD-3-Clause
# SPDX-License-Identifier: LGPL-3.0-only
proc get_slot_field {slot_output shard_id node_id attrib_id} {
return [lindex [lindex [lindex $slot_output $shard_id] $node_id] $attrib_id]
}
# Start a cluster with 3 masters and 4 replicas.
# These tests rely on specific node ordering, so make sure no node fails over.
start_cluster 3 4 {tags {external:skip cluster} overrides {cluster-replica-no-failover yes}} {
test "Set cluster hostnames and verify they are propagated" {
for {set j 0} {$j < [llength $::servers]} {incr j} {
R $j config set cluster-announce-hostname "host-$j.com"
}
wait_for_condition 50 100 {
[are_hostnames_propagated "host-*.com"] eq 1
} else {
fail "cluster hostnames were not propagated"
}
# Now that everything is propagated, assert everyone agrees
wait_for_cluster_propagation
}
test "Update hostnames and make sure they are all eventually propagated" {
for {set j 0} {$j < [llength $::servers]} {incr j} {
R $j config set cluster-announce-hostname "host-updated-$j.com"
}
wait_for_condition 50 100 {
[are_hostnames_propagated "host-updated-*.com"] eq 1
} else {
fail "cluster hostnames were not propagated"
}
# Now that everything is propagated, assert everyone agrees
wait_for_cluster_propagation
}
test "Remove hostnames and make sure they are all eventually propagated" {
for {set j 0} {$j < [llength $::servers]} {incr j} {
R $j config set cluster-announce-hostname ""
}
wait_for_condition 50 100 {
[are_hostnames_propagated ""] eq 1
} else {
fail "cluster hostnames were not propagated"
}
# Now that everything is propagated, assert everyone agrees
wait_for_cluster_propagation
}
test "Verify cluster-preferred-endpoint-type behavior for redirects and info" {
R 0 config set cluster-announce-hostname "me.com"
R 1 config set cluster-announce-hostname ""
R 2 config set cluster-announce-hostname "them.com"
wait_for_cluster_propagation
# Verify default behavior
set slot_result [R 0 cluster slots]
assert_equal "" [lindex [get_slot_field $slot_result 0 2 0] 1]
assert_equal "" [lindex [get_slot_field $slot_result 2 2 0] 1]
assert_equal "hostname" [lindex [get_slot_field $slot_result 0 2 3] 0]
assert_equal "me.com" [lindex [get_slot_field $slot_result 0 2 3] 1]
assert_equal "hostname" [lindex [get_slot_field $slot_result 2 2 3] 0]
assert_equal "them.com" [lindex [get_slot_field $slot_result 2 2 3] 1]
# Redirect will use the IP address
catch {R 0 set foo foo} redir_err
assert_match "MOVED * 127.0.0.1:*" $redir_err
# Verify prefer hostname behavior
R 0 config set cluster-preferred-endpoint-type hostname
set slot_result [R 0 cluster slots]
assert_equal "me.com" [get_slot_field $slot_result 0 2 0]
assert_equal "them.com" [get_slot_field $slot_result 2 2 0]
# Redirect should use hostname
catch {R 0 set foo foo} redir_err
assert_match "MOVED * them.com:*" $redir_err
# Redirect to an unknown hostname returns ?
catch {R 0 set barfoo bar} redir_err
assert_match "MOVED * ?:*" $redir_err
# Verify unknown hostname behavior
R 0 config set cluster-preferred-endpoint-type unknown-endpoint
# Verify default behavior
set slot_result [R 0 cluster slots]
assert_equal "ip" [lindex [get_slot_field $slot_result 0 2 3] 0]
assert_equal "127.0.0.1" [lindex [get_slot_field $slot_result 0 2 3] 1]
assert_equal "ip" [lindex [get_slot_field $slot_result 2 2 3] 0]
assert_equal "127.0.0.1" [lindex [get_slot_field $slot_result 2 2 3] 1]
assert_equal "ip" [lindex [get_slot_field $slot_result 1 2 3] 0]
assert_equal "127.0.0.1" [lindex [get_slot_field $slot_result 1 2 3] 1]
# Not required by the protocol, but IP comes before hostname
assert_equal "hostname" [lindex [get_slot_field $slot_result 0 2 3] 2]
assert_equal "me.com" [lindex [get_slot_field $slot_result 0 2 3] 3]
assert_equal "hostname" [lindex [get_slot_field $slot_result 2 2 3] 2]
assert_equal "them.com" [lindex [get_slot_field $slot_result 2 2 3] 3]
# This node doesn't have a hostname
assert_equal 2 [llength [get_slot_field $slot_result 1 2 3]]
# Redirect should use empty string
catch {R 0 set foo foo} redir_err
assert_match "MOVED * :*" $redir_err
R 0 config set cluster-preferred-endpoint-type ip
}
test "Verify the nodes configured with prefer hostname only show hostname for new nodes" {
# Have everyone forget node 6 and isolate it from the cluster.
isolate_node 6
# Set hostnames for the masters, now that the node is isolated
R 0 config set cluster-announce-hostname "shard-1.com"
R 1 config set cluster-announce-hostname "shard-2.com"
R 2 config set cluster-announce-hostname "shard-3.com"
# Prevent Node 0 and Node 6 from properly meeting,
# they'll hang in the handshake phase. This allows us to
# test the case where we "know" about it but haven't
# successfully retrieved information about it yet.
R 0 DEBUG DROP-CLUSTER-PACKET-FILTER 0
R 6 DEBUG DROP-CLUSTER-PACKET-FILTER 0
# Have a replica meet the isolated node
R 3 cluster meet 127.0.0.1 [srv -6 port]
# Wait for the isolated node to learn about the rest of the cluster,
# which correspond to a single entry in cluster nodes. Note this
# doesn't mean the isolated node has successfully contacted each
# node.
wait_for_condition 50 100 {
[llength [split [R 6 CLUSTER NODES] "\n"]] eq [expr [llength $::servers] + 1]
} else {
fail "Isolated node didn't learn about the rest of the cluster *"
}
# Now, we wait until the two nodes that aren't filtering packets
# to accept our isolated nodes connections. At this point they will
# start showing up in cluster slots.
wait_for_condition 50 100 {
[llength [R 6 CLUSTER SLOTS]] eq 2
} else {
fail "Node did not learn about the 2 shards it can talk to"
}
set slot_result [R 6 CLUSTER SLOTS]
assert_equal [lindex [get_slot_field $slot_result 0 2 3] 1] "shard-2.com"
assert_equal [lindex [get_slot_field $slot_result 1 2 3] 1] "shard-3.com"
# Also make sure we know about the isolated master, we
# just can't reach it.
set master_id [R 0 CLUSTER MYID]
assert_match "*$master_id*" [R 6 CLUSTER NODES]
# Stop dropping cluster packets, and make sure everything
# stabilizes
R 0 DEBUG DROP-CLUSTER-PACKET-FILTER -1
R 6 DEBUG DROP-CLUSTER-PACKET-FILTER -1
# This operation sometimes spikes to around 5 seconds to resolve the state,
# so it has a higher timeout.
wait_for_condition 50 500 {
[llength [R 6 CLUSTER SLOTS]] eq 3
} else {
fail "Node did not learn about the 2 shards it can talk to"
}
set slot_result [R 6 CLUSTER SLOTS]
assert_equal [lindex [get_slot_field $slot_result 0 2 3] 1] "shard-1.com"
assert_equal [lindex [get_slot_field $slot_result 1 2 3] 1] "shard-2.com"
assert_equal [lindex [get_slot_field $slot_result 2 2 3] 1] "shard-3.com"
}
test "Test restart will keep hostname information" {
# Set a new hostname, reboot and make sure it sticks
R 0 config set cluster-announce-hostname "restart-1.com"
# Store the hostname in the config
R 0 config rewrite
restart_server 0 true false
set slot_result [R 0 CLUSTER SLOTS]
assert_equal [lindex [get_slot_field $slot_result 0 2 3] 1] "restart-1.com"
# As a sanity check, make sure everyone eventually agrees
wait_for_cluster_propagation
}
test "Test hostname validation" {
catch {R 0 config set cluster-announce-hostname [string repeat x 256]} err
assert_match "*Hostnames must be less than 256 characters*" $err
catch {R 0 config set cluster-announce-hostname "?.com"} err
assert_match "*Hostnames may only contain alphanumeric characters, hyphens or dots*" $err
# Note this isn't a valid hostname, but it passes our internal validation
R 0 config set cluster-announce-hostname "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-."
}
}