server.tcl 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. set ::global_overrides {}
  2. set ::tags {}
  3. proc error_and_quit {config_file error} {
  4. puts "!!COULD NOT START REDIS-SERVER\n"
  5. puts "CONFIGURATION:"
  6. puts [exec cat $config_file]
  7. puts "\nERROR:"
  8. puts [string trim $error]
  9. exit 1
  10. }
  11. proc check_valgrind_errors stderr {
  12. set fd [open $stderr]
  13. set buf [read $fd]
  14. close $fd
  15. if {![regexp -- {ERROR SUMMARY: 0 errors} $buf] ||
  16. ![regexp -- {definitely lost: 0 bytes} $buf]} {
  17. puts "*** VALGRIND ERRORS ***"
  18. puts $buf
  19. puts "--- press enter to continue ---"
  20. gets stdin
  21. }
  22. }
  23. proc kill_server config {
  24. # nothing to kill when running against external server
  25. if {$::external} return
  26. # nevermind if its already dead
  27. if {![is_alive $config]} { return }
  28. set pid [dict get $config pid]
  29. # check for leaks
  30. if {![dict exists $config "skipleaks"]} {
  31. catch {
  32. if {[string match {*Darwin*} [exec uname -a]]} {
  33. tags {"leaks"} {
  34. test "Check for memory leaks (pid $pid)" {
  35. exec leaks $pid
  36. } {*0 leaks*}
  37. }
  38. }
  39. }
  40. }
  41. # kill server and wait for the process to be totally exited
  42. while {[is_alive $config]} {
  43. if {[incr wait 10] % 1000 == 0} {
  44. puts "Waiting for process $pid to exit..."
  45. }
  46. catch {exec kill $pid}
  47. after 10
  48. }
  49. # Check valgrind errors if needed
  50. if {$::valgrind} {
  51. check_valgrind_errors [dict get $config stderr]
  52. }
  53. }
  54. proc is_alive config {
  55. set pid [dict get $config pid]
  56. if {[catch {exec ps -p $pid} err]} {
  57. return 0
  58. } else {
  59. return 1
  60. }
  61. }
  62. proc ping_server {host port} {
  63. set retval 0
  64. if {[catch {
  65. set fd [socket $::host $::port]
  66. fconfigure $fd -translation binary
  67. puts $fd "PING\r\n"
  68. flush $fd
  69. set reply [gets $fd]
  70. if {[string range $reply 0 4] eq {+PONG} ||
  71. [string range $reply 0 3] eq {-ERR}} {
  72. set retval 1
  73. }
  74. close $fd
  75. } e]} {
  76. if {$::verbose} {
  77. puts -nonewline "."
  78. }
  79. } else {
  80. if {$::verbose} {
  81. puts -nonewline "ok"
  82. }
  83. }
  84. return $retval
  85. }
  86. # doesn't really belong here, but highly coupled to code in start_server
  87. proc tags {tags code} {
  88. set ::tags [concat $::tags $tags]
  89. uplevel 1 $code
  90. set ::tags [lrange $::tags 0 end-[llength $tags]]
  91. }
  92. proc start_server {options {code undefined}} {
  93. # If we are runnign against an external server, we just push the
  94. # host/port pair in the stack the first time
  95. if {$::external} {
  96. if {[llength $::servers] == 0} {
  97. set srv {}
  98. dict set srv "host" $::host
  99. dict set srv "port" $::port
  100. set client [redis $::host $::port]
  101. dict set srv "client" $client
  102. $client select 9
  103. # append the server to the stack
  104. lappend ::servers $srv
  105. }
  106. uplevel 1 $code
  107. return
  108. }
  109. # setup defaults
  110. set baseconfig "default.conf"
  111. set overrides {}
  112. set tags {}
  113. # parse options
  114. foreach {option value} $options {
  115. switch $option {
  116. "config" {
  117. set baseconfig $value }
  118. "overrides" {
  119. set overrides $value }
  120. "tags" {
  121. set tags $value
  122. set ::tags [concat $::tags $value] }
  123. default {
  124. error "Unknown option $option" }
  125. }
  126. }
  127. set data [split [exec cat "tests/assets/$baseconfig"] "\n"]
  128. set config {}
  129. foreach line $data {
  130. if {[string length $line] > 0 && [string index $line 0] ne "#"} {
  131. set elements [split $line " "]
  132. set directive [lrange $elements 0 0]
  133. set arguments [lrange $elements 1 end]
  134. dict set config $directive $arguments
  135. }
  136. }
  137. # use a different directory every time a server is started
  138. dict set config dir [tmpdir server]
  139. # start every server on a different port
  140. dict set config port [incr ::port]
  141. # apply overrides from global space and arguments
  142. foreach {directive arguments} [concat $::global_overrides $overrides] {
  143. dict set config $directive $arguments
  144. }
  145. # write new configuration to temporary file
  146. set config_file [tmpfile redis.conf]
  147. set fp [open $config_file w+]
  148. foreach directive [dict keys $config] {
  149. puts -nonewline $fp "$directive "
  150. puts $fp [dict get $config $directive]
  151. }
  152. close $fp
  153. set stdout [format "%s/%s" [dict get $config "dir"] "stdout"]
  154. set stderr [format "%s/%s" [dict get $config "dir"] "stderr"]
  155. if {$::valgrind} {
  156. exec valgrind --suppressions=src/valgrind.sup src/redis-server $config_file > $stdout 2> $stderr &
  157. } else {
  158. exec src/redis-server $config_file > $stdout 2> $stderr &
  159. }
  160. # check that the server actually started
  161. # ugly but tries to be as fast as possible...
  162. set retrynum 20
  163. set serverisup 0
  164. if {$::verbose} {
  165. puts -nonewline "=== ($tags) Starting server ${::host}:${::port} "
  166. }
  167. after 10
  168. if {$code ne "undefined"} {
  169. while {[incr retrynum -1]} {
  170. catch {
  171. if {[ping_server $::host $::port]} {
  172. set serverisup 1
  173. }
  174. }
  175. if {$serverisup} break
  176. after 50
  177. }
  178. } else {
  179. set serverisup 1
  180. }
  181. if {$::verbose} {
  182. puts ""
  183. }
  184. if {!$serverisup} {
  185. error_and_quit $config_file [exec cat $stderr]
  186. }
  187. # find out the pid
  188. while {![info exists pid]} {
  189. regexp {^\[(\d+)\]} [exec head -n1 $stdout] _ pid
  190. after 100
  191. }
  192. # setup properties to be able to initialize a client object
  193. set host $::host
  194. set port $::port
  195. if {[dict exists $config bind]} { set host [dict get $config bind] }
  196. if {[dict exists $config port]} { set port [dict get $config port] }
  197. # setup config dict
  198. dict set srv "config_file" $config_file
  199. dict set srv "config" $config
  200. dict set srv "pid" $pid
  201. dict set srv "host" $host
  202. dict set srv "port" $port
  203. dict set srv "stdout" $stdout
  204. dict set srv "stderr" $stderr
  205. # if a block of code is supplied, we wait for the server to become
  206. # available, create a client object and kill the server afterwards
  207. if {$code ne "undefined"} {
  208. set line [exec head -n1 $stdout]
  209. if {[string match {*already in use*} $line]} {
  210. error_and_quit $config_file $line
  211. }
  212. while 1 {
  213. # check that the server actually started and is ready for connections
  214. if {[exec cat $stdout | grep "ready to accept" | wc -l] > 0} {
  215. break
  216. }
  217. after 10
  218. }
  219. # append the server to the stack
  220. lappend ::servers $srv
  221. # connect client (after server dict is put on the stack)
  222. reconnect
  223. # execute provided block
  224. set num_tests $::num_tests
  225. if {[catch { uplevel 1 $code } error]} {
  226. set backtrace $::errorInfo
  227. # Kill the server without checking for leaks
  228. dict set srv "skipleaks" 1
  229. kill_server $srv
  230. # Print warnings from log
  231. puts [format "\nLogged warnings (pid %d):" [dict get $srv "pid"]]
  232. set warnings [warnings_from_file [dict get $srv "stdout"]]
  233. if {[string length $warnings] > 0} {
  234. puts "$warnings"
  235. } else {
  236. puts "(none)"
  237. }
  238. puts ""
  239. error $error $backtrace
  240. }
  241. # Don't do the leak check when no tests were run
  242. if {$num_tests == $::num_tests} {
  243. dict set srv "skipleaks" 1
  244. }
  245. # pop the server object
  246. set ::servers [lrange $::servers 0 end-1]
  247. set ::tags [lrange $::tags 0 end-[llength $tags]]
  248. kill_server $srv
  249. } else {
  250. set ::tags [lrange $::tags 0 end-[llength $tags]]
  251. set _ $srv
  252. }
  253. }