summaryrefslogtreecommitdiff
path: root/examples/redis-unstable/tests/instances.tcl
diff options
context:
space:
mode:
authorMitja Felicijan <mitja.felicijan@gmail.com>2026-01-21 22:52:54 +0100
committerMitja Felicijan <mitja.felicijan@gmail.com>2026-01-21 22:52:54 +0100
commitdcacc00e3750300617ba6e16eb346713f91a783a (patch)
tree38e2d4fb5ed9d119711d4295c6eda4b014af73fd /examples/redis-unstable/tests/instances.tcl
parent58dac10aeb8f5a041c46bddbeaf4c7966a99b998 (diff)
downloadcrep-dcacc00e3750300617ba6e16eb346713f91a783a.tar.gz
Remove testing data
Diffstat (limited to 'examples/redis-unstable/tests/instances.tcl')
-rw-r--r--examples/redis-unstable/tests/instances.tcl749
1 files changed, 0 insertions, 749 deletions
diff --git a/examples/redis-unstable/tests/instances.tcl b/examples/redis-unstable/tests/instances.tcl
deleted file mode 100644
index d93b36b..0000000
--- a/examples/redis-unstable/tests/instances.tcl
+++ /dev/null
@@ -1,749 +0,0 @@
-# Multi-instance test framework.
-# This is used in order to test Sentinel and Redis Cluster, and provides
-# basic capabilities for spawning and handling N parallel Redis / Sentinel
-# instances.
-#
-# Copyright (C) 2014-Present, Redis Ltd.
-# All Rights reserved.
-#
-# Licensed under your choice of (a) the Redis Source Available License 2.0
-# (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the
-# GNU Affero General Public License v3 (AGPLv3).
-
-package require Tcl 8.5
-
-set tcl_precision 17
-source ../support/redis.tcl
-source ../support/util.tcl
-source ../support/aofmanifest.tcl
-source ../support/server.tcl
-source ../support/test.tcl
-
-set ::verbose 0
-set ::valgrind 0
-set ::tls 0
-set ::tls_module 0
-set ::pause_on_error 0
-set ::dont_clean 0
-set ::simulate_error 0
-set ::failed 0
-set ::sentinel_instances {}
-set ::redis_instances {}
-set ::global_config {}
-set ::sentinel_base_port 20000
-set ::redis_base_port 30000
-set ::redis_port_count 1024
-set ::host "127.0.0.1"
-set ::leaked_fds_file [file normalize "tmp/leaked_fds.txt"]
-set ::pids {} ; # We kill everything at exit
-set ::dirs {} ; # We remove all the temp dirs at exit
-set ::run_matching {} ; # If non empty, only tests matching pattern are run.
-set ::stop_on_failure 0
-set ::loop 0
-set ::tsan 0
-
-if {[catch {cd tmp}]} {
- puts "tmp directory not found."
- puts "Please run this test from the Redis source root."
- exit 1
-}
-
-# Execute the specified instance of the server specified by 'type', using
-# the provided configuration file. Returns the PID of the process.
-proc exec_instance {type dirname cfgfile} {
- if {$type eq "redis"} {
- set prgname redis-server
- } elseif {$type eq "sentinel"} {
- set prgname redis-sentinel
- } else {
- error "Unknown instance type."
- }
-
- set errfile [file join $dirname err.txt]
- if {$::valgrind} {
- set pid [exec valgrind --track-origins=yes --suppressions=../../../src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full ../../../src/${prgname} $cfgfile 2>> $errfile &]
- } else {
- set pid [exec ../../../src/${prgname} $cfgfile 2>> $errfile &]
- }
- return $pid
-}
-
-# Spawn a redis or sentinel instance, depending on 'type'.
-proc spawn_instance {type base_port count {conf {}} {base_conf_file ""}} {
- for {set j 0} {$j < $count} {incr j} {
- set port [find_available_port $base_port $::redis_port_count]
- # plaintext port (only used for TLS cluster)
- set pport 0
- # Create a directory for this instance.
- set dirname "${type}_${j}"
- lappend ::dirs $dirname
- catch {exec rm -rf $dirname}
- file mkdir $dirname
-
- # Write the instance config file.
- set cfgfile [file join $dirname $type.conf]
- if {$base_conf_file ne ""} {
- file copy -- $base_conf_file $cfgfile
- set cfg [open $cfgfile a+]
- } else {
- set cfg [open $cfgfile w]
- }
-
- if {$::tls} {
- if {$::tls_module} {
- puts $cfg [format "loadmodule %s/../../../src/redis-tls.so" [pwd]]
- }
-
- puts $cfg "tls-port $port"
- puts $cfg "tls-replication yes"
- puts $cfg "tls-cluster yes"
- # plaintext port, only used by plaintext clients in a TLS cluster
- set pport [find_available_port $base_port $::redis_port_count]
- puts $cfg "port $pport"
- puts $cfg [format "tls-cert-file %s/../../tls/server.crt" [pwd]]
- puts $cfg [format "tls-key-file %s/../../tls/server.key" [pwd]]
- puts $cfg [format "tls-client-cert-file %s/../../tls/client.crt" [pwd]]
- puts $cfg [format "tls-client-key-file %s/../../tls/client.key" [pwd]]
- puts $cfg [format "tls-dh-params-file %s/../../tls/redis.dh" [pwd]]
- puts $cfg [format "tls-ca-cert-file %s/../../tls/ca.crt" [pwd]]
- } else {
- puts $cfg "port $port"
- }
-
- if {$::log_req_res} {
- puts $cfg "req-res-logfile stdout.reqres"
- }
-
- if {$::force_resp3} {
- puts $cfg "client-default-resp 3"
- }
-
- puts $cfg "repl-diskless-sync-delay 0"
- puts $cfg "dir ./$dirname"
- puts $cfg "logfile log.txt"
- # Add additional config files
- foreach directive $conf {
- puts $cfg $directive
- }
- dict for {name val} $::global_config {
- puts $cfg "$name $val"
- }
- close $cfg
-
- # Finally exec it and remember the pid for later cleanup.
- set retry 100
- while {$retry} {
- set pid [exec_instance $type $dirname $cfgfile]
-
- # Check availability
- if {[server_is_up 127.0.0.1 $port 100] == 0} {
- puts "Starting $type #$j at port $port failed, try another"
- incr retry -1
- set port [find_available_port $base_port $::redis_port_count]
- set cfg [open $cfgfile a+]
- if {$::tls} {
- puts $cfg "tls-port $port"
- set pport [find_available_port $base_port $::redis_port_count]
- puts $cfg "port $pport"
- } else {
- puts $cfg "port $port"
- }
- close $cfg
- } else {
- puts "Starting $type #$j at port $port"
- lappend ::pids $pid
- break
- }
- }
-
- # Check availability finally
- if {[server_is_up $::host $port 100] == 0} {
- set logfile [file join $dirname log.txt]
- puts [exec tail $logfile]
- abort_sentinel_test "Problems starting $type #$j: ping timeout, maybe server start failed, check $logfile"
- }
-
- # Push the instance into the right list
- set link [redis $::host $port 0 $::tls]
- $link reconnect 1
- lappend ::${type}_instances [list \
- pid $pid \
- host $::host \
- port $port \
- plaintext-port $pport \
- link $link \
- ]
- }
-}
-
-proc log_crashes {} {
- set start_pattern {*REDIS BUG REPORT START*}
- set logs [glob */log.txt]
- foreach log $logs {
- set fd [open $log]
- set found 0
- while {[gets $fd line] >= 0} {
- if {[string match $start_pattern $line]} {
- puts "\n*** Crash report found in $log ***"
- set found 1
- }
- if {$found} {
- puts $line
- incr ::failed
- }
- }
- }
-
- set logs [glob */err.txt]
- foreach log $logs {
- set res [find_valgrind_errors $log true]
- if {$res != ""} {
- puts $res
- incr ::failed
- }
- }
-
- set logs [glob */err.txt]
- foreach log $logs {
- set res [sanitizer_errors_from_file $log]
- if {$res != ""} {
- puts $res
- incr ::failed
- }
- }
-}
-
-proc is_alive pid {
- if {[catch {exec ps -p $pid} err]} {
- return 0
- } else {
- return 1
- }
-}
-
-proc stop_instance pid {
- # Node might have been stopped in the test
- # Send SIGCONT before SIGTERM, otherwise shutdown may be slow with ASAN.
- catch {exec kill -SIGCONT $pid}
- catch {exec kill $pid}
- if {$::valgrind} {
- set max_wait 120000
- } else {
- set max_wait 10000
- }
- while {[is_alive $pid]} {
- incr wait 10
-
- if {$wait == $max_wait} {
- puts [colorstr red "Forcing process $pid to crash..."]
- catch {exec kill -SEGV $pid}
- } elseif {$wait >= $max_wait * 2} {
- puts [colorstr red "Forcing process $pid to exit..."]
- catch {exec kill -KILL $pid}
- } elseif {$wait % 1000 == 0} {
- puts "Waiting for process $pid to exit..."
- }
- after 10
- }
-}
-
-proc cleanup {} {
- puts "Cleaning up..."
- foreach pid $::pids {
- puts "killing stale instance $pid"
- stop_instance $pid
- }
- log_crashes
- if {$::dont_clean} {
- return
- }
- foreach dir $::dirs {
- catch {exec rm -rf $dir}
- }
-}
-
-proc abort_sentinel_test msg {
- incr ::failed
- puts "WARNING: Aborting the test."
- puts ">>>>>>>> $msg"
- if {$::pause_on_error} pause_on_error
- cleanup
- exit 1
-}
-
-proc parse_options {} {
- for {set j 0} {$j < [llength $::argv]} {incr j} {
- set opt [lindex $::argv $j]
- set val [lindex $::argv [expr $j+1]]
- if {$opt eq "--single"} {
- incr j
- lappend ::run_matching "*${val}*"
- } elseif {$opt eq "--pause-on-error"} {
- set ::pause_on_error 1
- } elseif {$opt eq {--dont-clean}} {
- set ::dont_clean 1
- } elseif {$opt eq "--fail"} {
- set ::simulate_error 1
- } elseif {$opt eq {--valgrind}} {
- set ::valgrind 1
- } elseif {$opt eq {--host}} {
- incr j
- set ::host ${val}
- } elseif {$opt eq {--tls} || $opt eq {--tls-module}} {
- package require tls 1.6
- ::tls::init \
- -cafile "$::tlsdir/ca.crt" \
- -certfile "$::tlsdir/client.crt" \
- -keyfile "$::tlsdir/client.key"
- set ::tls 1
- if {$opt eq {--tls-module}} {
- set ::tls_module 1
- }
- } elseif {$opt eq {--config}} {
- set val2 [lindex $::argv [expr $j+2]]
- dict set ::global_config $val $val2
- incr j 2
- } elseif {$opt eq {--stop}} {
- set ::stop_on_failure 1
- } elseif {$opt eq {--loop}} {
- set ::loop 1
- } elseif {$opt eq {--log-req-res}} {
- set ::log_req_res 1
- } elseif {$opt eq {--force-resp3}} {
- set ::force_resp3 1
- } elseif {$opt eq {--tsan}} {
- set ::tsan 1
- } elseif {$opt eq "--help"} {
- puts "--single <pattern> Only runs tests specified by pattern."
- puts "--dont-clean Keep log files on exit."
- puts "--pause-on-error Pause for manual inspection on error."
- puts "--fail Simulate a test failure."
- puts "--valgrind Run with valgrind."
- puts "--tls Run tests in TLS mode."
- puts "--tls-module Run tests in TLS mode with Redis module."
- puts "--host <host> Use hostname instead of 127.0.0.1."
- puts "--config <k> <v> Extra config argument(s)."
- puts "--stop Blocks once the first test fails."
- puts "--loop Execute the specified set of tests forever."
- puts "--help Shows this help."
- exit 0
- } else {
- puts "Unknown option $opt"
- exit 1
- }
- }
-}
-
-# If --pause-on-error option was passed at startup this function is called
-# on error in order to give the developer a chance to understand more about
-# the error condition while the instances are still running.
-proc pause_on_error {} {
- puts ""
- puts [colorstr yellow "*** Please inspect the error now ***"]
- puts "\nType \"continue\" to resume the test, \"help\" for help screen.\n"
- while 1 {
- puts -nonewline "> "
- flush stdout
- set line [gets stdin]
- set argv [split $line " "]
- set cmd [lindex $argv 0]
- if {$cmd eq {continue}} {
- break
- } elseif {$cmd eq {show-redis-logs}} {
- set count 10
- if {[lindex $argv 1] ne {}} {set count [lindex $argv 1]}
- foreach_redis_id id {
- puts "=== REDIS $id ===="
- puts [exec tail -$count redis_$id/log.txt]
- puts "---------------------\n"
- }
- } elseif {$cmd eq {show-sentinel-logs}} {
- set count 10
- if {[lindex $argv 1] ne {}} {set count [lindex $argv 1]}
- foreach_sentinel_id id {
- puts "=== SENTINEL $id ===="
- puts [exec tail -$count sentinel_$id/log.txt]
- puts "---------------------\n"
- }
- } elseif {$cmd eq {ls}} {
- foreach_redis_id id {
- puts -nonewline "Redis $id"
- set errcode [catch {
- set str {}
- append str "@[RI $id tcp_port]: "
- append str "[RI $id role] "
- if {[RI $id role] eq {slave}} {
- append str "[RI $id master_host]:[RI $id master_port]"
- }
- set str
- } retval]
- if {$errcode} {
- puts " -- $retval"
- } else {
- puts $retval
- }
- }
- foreach_sentinel_id id {
- puts -nonewline "Sentinel $id"
- set errcode [catch {
- set str {}
- append str "@[SI $id tcp_port]: "
- append str "[join [S $id sentinel get-master-addr-by-name mymaster]]"
- set str
- } retval]
- if {$errcode} {
- puts " -- $retval"
- } else {
- puts $retval
- }
- }
- } elseif {$cmd eq {help}} {
- puts "ls List Sentinel and Redis instances."
- puts "show-sentinel-logs \[N\] Show latest N lines of logs."
- puts "show-redis-logs \[N\] Show latest N lines of logs."
- puts "S <id> cmd ... arg Call command in Sentinel <id>."
- puts "R <id> cmd ... arg Call command in Redis <id>."
- puts "SI <id> <field> Show Sentinel <id> INFO <field>."
- puts "RI <id> <field> Show Redis <id> INFO <field>."
- puts "continue Resume test."
- } else {
- set errcode [catch {eval $line} retval]
- if {$retval ne {}} {puts "$retval"}
- }
- }
-}
-
-# We redefine 'test' as for Sentinel we don't use the server-client
-# architecture for the test, everything is sequential.
-proc test {descr code} {
- set ts [clock format [clock seconds] -format %H:%M:%S]
- puts -nonewline "$ts> $descr: "
- flush stdout
-
- if {[catch {set retval [uplevel 1 $code]} error]} {
- incr ::failed
- if {[string match "assertion:*" $error]} {
- set msg "FAILED: [string range $error 10 end]"
- puts [colorstr red $msg]
- if {$::pause_on_error} pause_on_error
- puts [colorstr red "(Jumping to next unit after error)"]
- return -code continue
- } else {
- # Re-raise, let handler up the stack take care of this.
- error $error $::errorInfo
- }
- } else {
- puts [colorstr green OK]
- }
-}
-
-# Check memory leaks when running on OSX using the "leaks" utility.
-proc check_leaks instance_types {
- if {[string match {*Darwin*} [exec uname -a]]} {
- puts -nonewline "Testing for memory leaks..."; flush stdout
- foreach type $instance_types {
- foreach_instance_id [set ::${type}_instances] id {
- if {[instance_is_killed $type $id]} continue
- set pid [get_instance_attrib $type $id pid]
- set output {0 leaks}
- catch {exec leaks $pid} output
- if {[string match {*process does not exist*} $output] ||
- [string match {*cannot examine*} $output]} {
- # In a few tests we kill the server process.
- set output "0 leaks"
- } else {
- puts -nonewline "$type/$pid "
- flush stdout
- }
- if {![string match {*0 leaks*} $output]} {
- puts [colorstr red "=== MEMORY LEAK DETECTED ==="]
- puts "Instance type $type, ID $id:"
- puts $output
- puts "==="
- incr ::failed
- }
- }
- }
- puts ""
- }
-}
-
-# Execute all the units inside the 'tests' directory.
-proc run_tests {} {
- set tests [lsort [glob ../tests/*]]
-
-while 1 {
- foreach test $tests {
- # Remove leaked_fds file before starting
- if {$::leaked_fds_file != "" && [file exists $::leaked_fds_file]} {
- file delete $::leaked_fds_file
- }
-
- if {[llength $::run_matching] != 0 && ![search_pattern_list $test $::run_matching true]} {
- continue
- }
- if {[file isdirectory $test]} continue
- puts [colorstr yellow "Testing unit: [lindex [file split $test] end]"]
- if {[catch { source $test } err]} {
- puts "FAILED: caught an error in the test $err"
- puts $::errorInfo
- incr ::failed
- # letting the tests resume, so we'll eventually reach the cleanup and report crashes
-
- if {$::stop_on_failure} {
- puts -nonewline "(Test stopped, press enter to resume the tests)"
- flush stdout
- gets stdin
- }
- }
- check_leaks {redis sentinel}
-
- # Check if a leaked fds file was created and abort the test.
- if {$::leaked_fds_file != "" && [file exists $::leaked_fds_file]} {
- puts [colorstr red "ERROR: Sentinel has leaked fds to scripts:"]
- puts [exec cat $::leaked_fds_file]
- puts "----"
- incr ::failed
- }
- }
-
- if {$::loop == 0} { break }
-} ;# while 1
-}
-
-# Print a message and exists with 0 / 1 according to zero or more failures.
-proc end_tests {} {
- if {$::failed == 0 } {
- puts [colorstr green "GOOD! No errors."]
- exit 0
- } else {
- puts [colorstr red "WARNING $::failed test(s) failed."]
- exit 1
- }
-}
-
-# The "S" command is used to interact with the N-th Sentinel.
-# The general form is:
-#
-# S <sentinel-id> command arg arg arg ...
-#
-# Example to ping the Sentinel 0 (first instance): S 0 PING
-proc S {n args} {
- set s [lindex $::sentinel_instances $n]
- [dict get $s link] {*}$args
-}
-
-# Returns a Redis instance by index.
-# Example:
-# [Rn 0] info
-proc Rn {n} {
- return [dict get [lindex $::redis_instances $n] link]
-}
-
-# Like R but to chat with Redis instances.
-proc R {n args} {
- [Rn $n] {*}$args
-}
-
-proc get_info_field {info field} {
- set fl [string length $field]
- append field :
- foreach line [split $info "\n"] {
- set line [string trim $line "\r\n "]
- if {[string range $line 0 $fl] eq $field} {
- return [string range $line [expr {$fl+1}] end]
- }
- }
- return {}
-}
-
-proc SI {n field} {
- get_info_field [S $n info] $field
-}
-
-proc RI {n field} {
- get_info_field [R $n info] $field
-}
-
-proc RPort {n} {
- if {$::tls} {
- return [lindex [R $n config get tls-port] 1]
- } else {
- return [lindex [R $n config get port] 1]
- }
-}
-
-# Iterate over IDs of sentinel or redis instances.
-proc foreach_instance_id {instances idvar code} {
- upvar 1 $idvar id
- for {set id 0} {$id < [llength $instances]} {incr id} {
- set errcode [catch {uplevel 1 $code} result]
- if {$errcode == 1} {
- error $result $::errorInfo $::errorCode
- } elseif {$errcode == 4} {
- continue
- } elseif {$errcode == 3} {
- break
- } elseif {$errcode != 0} {
- return -code $errcode $result
- }
- }
-}
-
-proc foreach_sentinel_id {idvar code} {
- set errcode [catch {uplevel 1 [list foreach_instance_id $::sentinel_instances $idvar $code]} result]
- return -code $errcode $result
-}
-
-proc foreach_redis_id {idvar code} {
- set errcode [catch {uplevel 1 [list foreach_instance_id $::redis_instances $idvar $code]} result]
- return -code $errcode $result
-}
-
-# Get the specific attribute of the specified instance type, id.
-proc get_instance_attrib {type id attrib} {
- dict get [lindex [set ::${type}_instances] $id] $attrib
-}
-
-# Set the specific attribute of the specified instance type, id.
-proc set_instance_attrib {type id attrib newval} {
- set d [lindex [set ::${type}_instances] $id]
- dict set d $attrib $newval
- lset ::${type}_instances $id $d
-}
-
-# Create a master-slave cluster of the given number of total instances.
-# The first instance "0" is the master, all others are configured as
-# slaves.
-proc create_redis_master_slave_cluster n {
- foreach_redis_id id {
- if {$id == 0} {
- # Our master.
- R $id slaveof no one
- R $id flushall
- } elseif {$id < $n} {
- R $id slaveof [get_instance_attrib redis 0 host] \
- [get_instance_attrib redis 0 port]
- } else {
- # Instances not part of the cluster.
- R $id slaveof no one
- }
- }
- # Wait for all the slaves to sync.
- wait_for_condition 1000 50 {
- [RI 0 connected_slaves] == ($n-1)
- } else {
- fail "Unable to create a master-slaves cluster."
- }
-}
-
-proc get_instance_id_by_port {type port} {
- foreach_${type}_id id {
- if {[get_instance_attrib $type $id port] == $port} {
- return $id
- }
- }
- fail "Instance $type port $port not found."
-}
-
-# Kill an instance of the specified type/id with SIGKILL.
-# This function will mark the instance PID as -1 to remember that this instance
-# is no longer running and will remove its PID from the list of pids that
-# we kill at cleanup.
-#
-# The instance can be restarted with restart-instance.
-proc kill_instance {type id} {
- set pid [get_instance_attrib $type $id pid]
- set port [get_instance_attrib $type $id port]
-
- if {$pid == -1} {
- error "You tried to kill $type $id twice."
- }
-
- stop_instance $pid
- set_instance_attrib $type $id pid -1
- set_instance_attrib $type $id link you_tried_to_talk_with_killed_instance
-
- # Remove the PID from the list of pids to kill at exit.
- set ::pids [lsearch -all -inline -not -exact $::pids $pid]
-
- # Wait for the port it was using to be available again, so that's not
- # an issue to start a new server ASAP with the same port.
- set retry 100
- while {[incr retry -1]} {
- set port_is_free [catch {set s [socket 127.0.0.1 $port]}]
- if {$port_is_free} break
- catch {close $s}
- after 100
- }
- if {$retry == 0} {
- error "Port $port does not return available after killing instance."
- }
-}
-
-# Return true of the instance of the specified type/id is killed.
-proc instance_is_killed {type id} {
- set pid [get_instance_attrib $type $id pid]
- expr {$pid == -1}
-}
-
-# Restart an instance previously killed by kill_instance
-proc restart_instance {type id} {
- set dirname "${type}_${id}"
- set cfgfile [file join $dirname $type.conf]
- set port [get_instance_attrib $type $id port]
-
- # Execute the instance with its old setup and append the new pid
- # file for cleanup.
- set pid [exec_instance $type $dirname $cfgfile]
- set_instance_attrib $type $id pid $pid
- lappend ::pids $pid
-
- # Check that the instance is running
- if {[server_is_up 127.0.0.1 $port 100] == 0} {
- set logfile [file join $dirname log.txt]
- puts [exec tail $logfile]
- abort_sentinel_test "Problems starting $type #$id: ping timeout, maybe server start failed, check $logfile"
- }
-
- # Connect with it with a fresh link
- set link [redis 127.0.0.1 $port 0 $::tls]
- $link reconnect 1
- set_instance_attrib $type $id link $link
-
- # Make sure the instance is not loading the dataset when this
- # function returns.
- while 1 {
- catch {[$link ping]} retval
- if {[string match {*LOADING*} $retval]} {
- after 100
- continue
- } else {
- break
- }
- }
-}
-
-proc redis_deferring_client {type id} {
- set port [get_instance_attrib $type $id port]
- set host [get_instance_attrib $type $id host]
- set client [redis $host $port 1 $::tls]
- return $client
-}
-
-proc redis_deferring_client_by_addr {host port} {
- set client [redis $host $port 1 $::tls]
- return $client
-}
-
-proc redis_client {type id} {
- set port [get_instance_attrib $type $id port]
- set host [get_instance_attrib $type $id host]
- set client [redis $host $port 0 $::tls]
- return $client
-}
-
-proc redis_client_by_addr {host port} {
- set client [redis $host $port 0 $::tls]
- return $client
-}