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-." } }