diff --git a/src/commands/sentinel-debug.json b/src/commands/sentinel-debug.json index 186b1df9db8..c671ec5e1a4 100644 --- a/src/commands/sentinel-debug.json +++ b/src/commands/sentinel-debug.json @@ -12,6 +12,21 @@ "SENTINEL", "ONLY_SENTINEL" ], + "reply_schema": { + "oneOf": [ + { + "description": "The configuration update was successful.", + "const": "OK" + }, + { + "description": "List of configurable time parameters and their values (milliseconds).", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + ] + }, "arguments": [ { "name": "data", diff --git a/src/commands/sentinel-info-cache.json b/src/commands/sentinel-info-cache.json index 29af975bc2d..af89f182eac 100644 --- a/src/commands/sentinel-info-cache.json +++ b/src/commands/sentinel-info-cache.json @@ -12,6 +12,47 @@ "SENTINEL", "ONLY_SENTINEL" ], + "reply_schema": { + "type": "array", + "description": "This is actually a map, the odd entries are a master name, and the even entries are the last cached INFO output from that master and all its replicas.", + "minItems": 0, + "maxItems": 4294967295, + "items": [ + { + "oneOf": [ + { + "type": "string", + "description": "The master name." + }, + { + "type": "array", + "description": "This is an array of pairs, the odd entries are the INFO age, and the even entries are the cached INFO string. The first pair belong to the master and the rest are its replicas.", + "minItems": 2, + "maxItems": 2, + "items": [ + { + "description": "The number of milliseconds since when the INFO was cached.", + "type": "integer" + }, + { + "description": "The cached INFO string or null.", + "oneOf": [ + { + "description": "The cached INFO string.", + "type": "string" + }, + { + "description": "No cached INFO string.", + "type": "null" + } + ] + } + ] + } + ] + } + ] + }, "arguments": [ { "name": "nodename", diff --git a/src/commands/sentinel-pending-scripts.json b/src/commands/sentinel-pending-scripts.json index 1413431493b..22dae4774dc 100644 --- a/src/commands/sentinel-pending-scripts.json +++ b/src/commands/sentinel-pending-scripts.json @@ -10,6 +10,43 @@ "ADMIN", "SENTINEL", "ONLY_SENTINEL" - ] + ], + "reply_schema": { + "type": "array", + "description": "List of pending scripts.", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "argv": { + "type": "array", + "description": "Script arguments.", + "items": { + "type": "string" + } + }, + "flags": { + "type": "string", + "description": "Script flags." + }, + "pid": { + "type": "string", + "description": "Script pid." + }, + "run-time": { + "type": "string", + "description": "Script run-time." + }, + "run-delay": { + "type": "string", + "description": "Script run-delay." + }, + "retry-num": { + "type": "string", + "description": "Number of times we tried to execute the script." + } + } + } + } } } diff --git a/src/commands/sentinel-reset.json b/src/commands/sentinel-reset.json index 14ccbd5a1b6..17b53a481f2 100644 --- a/src/commands/sentinel-reset.json +++ b/src/commands/sentinel-reset.json @@ -12,6 +12,10 @@ "SENTINEL", "ONLY_SENTINEL" ], + "reply_schema": { + "type": "integer", + "description": "The number of masters that were reset." + }, "arguments": [ { "name": "pattern", diff --git a/src/commands/sentinel-simulate-failure.json b/src/commands/sentinel-simulate-failure.json index 00aad668d2d..5031d44ae4f 100644 --- a/src/commands/sentinel-simulate-failure.json +++ b/src/commands/sentinel-simulate-failure.json @@ -11,6 +11,21 @@ "SENTINEL", "ONLY_SENTINEL" ], + "reply_schema": { + "oneOf": [ + { + "description": "The simulated flag was set.", + "const": "OK" + }, + { + "description": "Supported simulates flags. Returned in case `HELP` was used.", + "type": "array", + "items": { + "type": "string" + } + } + ] + }, "arguments": [ { "name": "mode", diff --git a/src/sentinel.c b/src/sentinel.c index 03482fdd2d2..3d5c3861c08 100644 --- a/src/sentinel.c +++ b/src/sentinel.c @@ -4071,11 +4071,14 @@ NULL } /* Reply format: - * 1.) master name - * 2.) 1.) info from master - * 2.) info from replica - * ... - * 3.) other master name + * 1) master name + * 2) 1) 1) info cached ms + * 2) info from master + * 2) 1) info cached ms + * 2) info from replica1 + * ... + * 3) other master name + * ... * ... */ addReplyArrayLen(c,dictSize(masters_local) * 2); diff --git a/tests/sentinel/tests/00-base.tcl b/tests/sentinel/tests/00-base.tcl index 1b33ca7a31c..b4d65751b55 100644 --- a/tests/sentinel/tests/00-base.tcl +++ b/tests/sentinel/tests/00-base.tcl @@ -31,6 +31,50 @@ test "Sentinel command flag infrastructure works correctly" { } } +test "SENTINEL HELP output the sentinel subcommand help" { + assert_match "*SENTINEL *" [S 0 SENTINEL HELP] +} + +test "SENTINEL MYID return the sentinel instance ID" { + assert_equal 40 [string length [S 0 SENTINEL MYID]] + assert_equal [S 0 SENTINEL MYID] [S 0 SENTINEL MYID] +} + +test "SENTINEL INFO CACHE returns the cached info" { + set res [S 0 SENTINEL INFO-CACHE mymaster] + assert_morethan_equal [llength $res] 2 + assert_equal "mymaster" [lindex $res 0] + + set res [lindex $res 1] + assert_morethan_equal [llength $res] 2 + assert_morethan [lindex $res 0] 0 + assert_match "*# Server*" [lindex $res 1] +} + +test "SENTINEL PENDING-SCRIPTS returns the information about pending scripts" { + # may or may not have a value, so assert greater than or equal to 0. + assert_morethan_equal [llength [S 0 SENTINEL PENDING-SCRIPTS]] 0 +} + +test "SENTINEL MASTERS returns a list of monitored masters" { + assert_match "*mymaster*" [S 0 SENTINEL MASTERS] + assert_morethan_equal [llength [S 0 SENTINEL MASTERS]] 1 +} + +test "SENTINEL SENTINELS returns a list of sentinel instances" { + assert_morethan_equal [llength [S 0 SENTINEL SENTINELS mymaster]] 1 +} + +test "SENTINEL SLAVES returns a list of the monitored replicas" { + assert_morethan_equal [llength [S 0 SENTINEL SLAVES mymaster]] 1 +} + +test "SENTINEL SIMULATE-FAILURE HELP list supported flags" { + set res [S 0 SENTINEL SIMULATE-FAILURE HELP] + assert_morethan_equal [llength $res] 2 + assert_equal {crash-after-election crash-after-promotion} $res +} + test "Basic failover works if the master is down" { set old_port [RPort $master_id] set addr [S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] @@ -149,3 +193,10 @@ test "Failover works if we configure for absolute agreement" { test "New master [join $addr {:}] role matches" { assert {[RI $master_id role] eq {master}} } + +test "SENTINEL RESET can resets the master" { + assert_equal 1 [S 0 SENTINEL RESET mymaster] + assert_equal 0 [llength [S 0 SENTINEL SENTINELS mymaster]] + assert_equal 0 [llength [S 0 SENTINEL SLAVES mymaster]] + assert_equal 0 [llength [S 0 SENTINEL REPLICAS mymaster]] +} diff --git a/tests/sentinel/tests/05-manual.tcl b/tests/sentinel/tests/05-manual.tcl index 72d80fdf80f..7e050b0dc49 100644 --- a/tests/sentinel/tests/05-manual.tcl +++ b/tests/sentinel/tests/05-manual.tcl @@ -61,3 +61,28 @@ test "The old master eventually gets reconfigured as a slave" { fail "Old master not reconfigured as slave of new master" } } + +foreach flag {crash-after-election crash-after-promotion} { + test "SENTINEL SIMULATE-FAILURE $flag works" { + assert_equal {OK} [S 0 SENTINEL SIMULATE-FAILURE $flag] + + # Trigger a failover, failover will trigger leader election, replica promotion + wait_for_condition 300 50 { + [catch {S 0 SENTINEL FAILOVER mymaster}] == 0 + } else { + catch {S 0 SENTINEL FAILOVER mymaster} reply + puts [S 0 SENTINEL REPLICAS mymaster] + fail "Sentinel manual failover did not work, got: $reply" + } + + # Wait for sentinel to exit (due to simulate-failure flags) + wait_for_condition 1000 50 { + [catch {S 0 PING}] == 1 + } else { + fail "Sentinel set $flag but did not exit" + } + assert_error {*couldn't open socket: connection refused*} {S 0 PING} + + restart_instance sentinel 0 + } +} diff --git a/utils/req-res-log-validator.py b/utils/req-res-log-validator.py index 1f11b030749..46c110019f0 100755 --- a/utils/req-res-log-validator.py +++ b/utils/req-res-log-validator.py @@ -58,17 +58,6 @@ # Commands to which we decided not write a reply schema "pfdebug", "lolwut", - # TODO: write a reply schema for the following commands - "sentinel|debug", - "sentinel|info-cache", - "sentinel|pending-scripts", - "sentinel|reset", - "sentinel|simulate-failure", - "sentinel|help", - "sentinel|masters", - "sentinel|myid", - "sentinel|sentinels", - "sentinel|slaves", } class Request(object):