futriix/tests/sentinel/tests/03-runtime-reconf.tcl
Wen Hui a2f4aedd23 Add 4 commands for sentinel and update most test cases and json files (#789)
Add 4 new commands for Sentinel (reference
https://github.com/valkey-io/valkey/issues/36)

Sentinel GET-PRIMARY-ADDR-BY-NAME
Sentinel PRIMARY
Sentinel PRIMARIES
Sentinel IS-PRIMARY-DOWN-BY-ADDR

and deprecate 4 old commands:

Sentinel GET-MASTER-ADDR-BY-NAME
Sentinel MASTER
Sentinel MASTERS
Sentinel IS-MASTER-DOWN-BY-ADDR

and all sentinel tests pass here
https://github.com/hwware/valkey/actions/runs/9962102363/job/27525124583

Note: 

1. runtest-sentinel pass all test cases
2. I finished a sentinel rolling upgrade test: 1 primary 2 replicas 3
sentinel
   there are 4 steps in this test scenario: 
step 1: all 3 sentinel nodes run old sentinel, shutdown primary, and
then new primary can be voted successfully.
step 2: replace sentinel 1 with new sentinel bin file, and then shutdown
primary, and then another new primary can be voted successfully
step 3: replace sentinel 2 with new sentinel bin file, and then shutdown
primary, and then another new primary can be voted successfully
step 4: replace sentinel 3 with new sentinel bin file, and then shutdown
primary, and then another new primary can be voted successfully
   
We can see, even mixed version sentinel running, whole system still
works.

---------

Signed-off-by: hwware <wen.hui.ware@gmail.com>
2024-09-03 09:00:45 -07:00

226 lines
8.3 KiB
Tcl

# Test runtime reconfiguration command SENTINEL SET.
source "../tests/includes/init-tests.tcl"
set num_sentinels [llength $::sentinel_instances]
set ::user "testuser"
set ::password "secret"
proc server_set_password {} {
foreach_valkey_id id {
assert_equal {OK} [R $id CONFIG SET requirepass $::password]
assert_equal {OK} [R $id AUTH $::password]
assert_equal {OK} [R $id CONFIG SET primaryauth $::password]
}
}
proc server_reset_password {} {
foreach_valkey_id id {
assert_equal {OK} [R $id CONFIG SET requirepass ""]
assert_equal {OK} [R $id CONFIG SET primaryauth ""]
}
}
proc server_set_acl {id} {
assert_equal {OK} [R $id ACL SETUSER $::user on >$::password allchannels +@all]
assert_equal {OK} [R $id ACL SETUSER default off]
R $id CLIENT KILL USER default SKIPME no
assert_equal {OK} [R $id AUTH $::user $::password]
assert_equal {OK} [R $id CONFIG SET primaryuser $::user]
assert_equal {OK} [R $id CONFIG SET primaryauth $::password]
}
proc server_reset_acl {id} {
assert_equal {OK} [R $id ACL SETUSER default on]
assert_equal {1} [R $id ACL DELUSER $::user]
assert_equal {OK} [R $id CONFIG SET primaryuser ""]
assert_equal {OK} [R $id CONFIG SET primaryauth ""]
}
proc verify_sentinel_connect_replicas {id} {
foreach replica [S $id SENTINEL REPLICAS mymaster] {
if {[string match "*disconnected*" [dict get $replica flags]]} {
return 0
}
}
return 1
}
proc wait_for_sentinels_connect_servers { {is_connect 1} } {
foreach_sentinel_id id {
wait_for_condition 1000 50 {
[string match "*disconnected*" [dict get [S $id SENTINEL PRIMARY mymaster] flags]] != $is_connect
} else {
fail "At least some sentinel can't connect to master"
}
wait_for_condition 1000 50 {
[verify_sentinel_connect_replicas $id] == $is_connect
} else {
fail "At least some sentinel can't connect to replica"
}
}
}
test "Sentinels (re)connection following SENTINEL SET myprimary auth-pass" {
# 3 types of sentinels to test:
# (re)started while primary changed pwd. Manage to connect only after setting pwd
set sent2re 0
# (up)dated in advance with primary new password
set sent2up 1
# (un)touched. Yet manage to maintain (old) connection
set sent2un 2
wait_for_sentinels_connect_servers
kill_instance sentinel $sent2re
server_set_password
assert_equal {OK} [S $sent2up SENTINEL SET mymaster auth-pass $::password]
restart_instance sentinel $sent2re
# Verify sentinel that restarted failed to connect master
wait_for_condition 100 50 {
[string match "*disconnected*" [dict get [S $sent2re SENTINEL PRIMARY mymaster] flags]] != 0
} else {
fail "Expected to be disconnected from master due to wrong password"
}
# Update restarted sentinel with master password
assert_equal {OK} [S $sent2re SENTINEL SET mymaster auth-pass $::password]
# All sentinels expected to connect successfully
wait_for_sentinels_connect_servers
# remove requirepass and verify sentinels manage to connect servers
server_reset_password
wait_for_sentinels_connect_servers
# Sanity check
verify_sentinel_auto_discovery
}
test "Sentinels (re)connection following primary ACL change" {
# Three types of sentinels to test during ACL change:
# 1. (re)started Sentinel. Manage to connect only after setting new pwd
# 2. (up)dated Sentinel, get just before ACL change the new password
# 3. (un)touched Sentinel that kept old connection with master and didn't
# set new ACL password won't persist ACL pwd change (unlike legacy auth-pass)
set sent2re 0
set sent2up 1
set sent2un 2
wait_for_sentinels_connect_servers
# kill sentinel 'sent2re' and restart it after ACL change
kill_instance sentinel $sent2re
# Update sentinel 'sent2up' with new user and pwd
assert_equal {OK} [S $sent2up SENTINEL SET mymaster auth-user $::user]
assert_equal {OK} [S $sent2up SENTINEL SET mymaster auth-pass $::password]
foreach_valkey_id id {
server_set_acl $id
}
restart_instance sentinel $sent2re
# Verify sentinel that restarted failed to reconnect master
wait_for_condition 100 50 {
[string match "*disconnected*" [dict get [S $sent2re SENTINEL PRIMARY mymaster] flags]] != 0
} else {
fail "Expected: Restarted sentinel to be disconnected from master due to obsolete password"
}
# Verify sentinel with updated password managed to connect (wait for sentinelTimer to reconnect)
wait_for_condition 100 50 {
[string match "*disconnected*" [dict get [S $sent2up SENTINEL PRIMARY mymaster] flags]] == 0
} else {
fail "Expected: Sentinel to be connected to master"
}
# Verify sentinel untouched gets failed to connect master
wait_for_condition 100 50 {
[string match "*disconnected*" [dict get [S $sent2un SENTINEL PRIMARY mymaster] flags]] != 0
} else {
fail "Expected: Sentinel to be disconnected from master due to obsolete password"
}
# Now update all sentinels with new password
foreach_sentinel_id id {
assert_equal {OK} [S $id SENTINEL SET mymaster auth-user $::user]
assert_equal {OK} [S $id SENTINEL SET mymaster auth-pass $::password]
}
# All sentinels expected to connect successfully
wait_for_sentinels_connect_servers
# remove requirepass and verify sentinels manage to connect servers
foreach_valkey_id id {
server_reset_acl $id
}
wait_for_sentinels_connect_servers
# Sanity check
verify_sentinel_auto_discovery
}
test "Set parameters in normal case" {
set info [S 0 SENTINEL primary mymaster]
set origin_quorum [dict get $info quorum]
set origin_down_after_milliseconds [dict get $info down-after-milliseconds]
set update_quorum [expr $origin_quorum+1]
set update_down_after_milliseconds [expr $origin_down_after_milliseconds+1000]
assert_equal [S 0 SENTINEL SET mymaster quorum $update_quorum] "OK"
assert_equal [S 0 SENTINEL SET mymaster down-after-milliseconds $update_down_after_milliseconds] "OK"
set update_info [S 0 SENTINEL primary mymaster]
assert {[dict get $update_info quorum] != $origin_quorum}
assert {[dict get $update_info down-after-milliseconds] != $origin_down_after_milliseconds}
#restore to origin config parameters
assert_equal [S 0 SENTINEL SET mymaster quorum $origin_quorum] "OK"
assert_equal [S 0 SENTINEL SET mymaster down-after-milliseconds $origin_down_after_milliseconds] "OK"
}
test "Set parameters in normal case with bad format" {
set info [S 0 SENTINEL primary mymaster]
set origin_down_after_milliseconds [dict get $info down-after-milliseconds]
assert_error "ERR Invalid argument '-20' for SENTINEL SET 'down-after-milliseconds'*" {S 0 SENTINEL SET mymaster down-after-milliseconds -20}
assert_error "ERR Invalid argument 'abc' for SENTINEL SET 'down-after-milliseconds'*" {S 0 SENTINEL SET mymaster down-after-milliseconds "abc"}
set current_info [S 0 SENTINEL primary mymaster]
assert {[dict get $current_info down-after-milliseconds] == $origin_down_after_milliseconds}
}
test "Sentinel Set with other error situations" {
# non-existing script
assert_error "ERR Notification script seems non existing*" {S 0 SENTINEL SET mymaster notification-script test.txt}
# wrong parameter number
assert_error "ERR wrong number of arguments for 'sentinel|set' command" {S 0 SENTINEL SET mymaster fakeoption}
# unknown parameter option
assert_error "ERR Unknown option or number of arguments for SENTINEL SET 'fakeoption'" {S 0 SENTINEL SET mymaster fakeoption fakevalue}
# save new config to disk failed
set info [S 0 SENTINEL primary mymaster]
set origin_quorum [dict get $info quorum]
set update_quorum [expr $origin_quorum+1]
set sentinel_id 0
set configfilename [file join "sentinel_$sentinel_id" "sentinel.conf"]
set configfilename_bak [file join "sentinel_$sentinel_id" "sentinel.conf.bak"]
file rename $configfilename $configfilename_bak
file mkdir $configfilename
catch {[S 0 SENTINEL SET mymaster quorum $update_quorum]} err
file delete $configfilename
file rename $configfilename_bak $configfilename
assert_match "ERR Failed to save config file*" $err
}