s.close()
+def port_is_free(addr, port):
+ """Assert that nothing is listening on addr:port.
+
+ Used by teardown to confirm a just-terminated rspamd has actually
+ released its listening sockets before the next suite on this pabot
+ worker reuses the same port range. `Wait For Process` only reaps the
+ main rspamd; the listening sockets are shared with forked workers and
+ can linger briefly after main exits. rspamd sets SO_REUSEADDR, so this
+ is NOT about TIME_WAIT -- a still-LISTENing socket from a not-yet-gone
+ worker genuinely fails the next bind() with EADDRINUSE. Connecting and
+ succeeding means someone is still listening -> raise so Wait Until
+ Keyword Succeeds retries; connection refused means the port is free.
+
+ Example:
+ | Wait Until Keyword Succeeds | 10s | 0.2s | Port Is Free | 127.0.0.1 | 56790 |
+ """
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.settimeout(0.5)
+ try:
+ s.connect((addr, int(port)))
+ except (ConnectionRefusedError, socket.timeout, OSError):
+ return True
+ finally:
+ s.close()
+ raise AssertionError("port %s:%s is still in use" % (addr, port))
+
+
def try_reap_zombies():
try:
os.waitpid(-1, os.WNOHANG)
END
Terminate Process ${RSPAMD_PROCESS}
Wait For Process ${RSPAMD_PROCESS}
+ # Wait For Process only reaps the main rspamd; its listening sockets are
+ # shared with forked workers and can linger a beat after main exits.
+ # Under pabot each worker runs many suites sequentially on the SAME port
+ # range, so if we release this suite before the kernel has closed the
+ # normal+controller sockets, the next suite's rspamd races them and dies
+ # with EADDRINUSE (rspamd sets SO_REUSEADDR, so this is a live socket,
+ # not TIME_WAIT). Block until the ports are actually free.
+ Wait For Rspamd Ports Released
Save Run Results ${RSPAMD_TMPDIR} configdump.stdout configdump.stderr rspamd.stderr rspamd.stdout rspamd.conf rspamd.log redis.log clickhouse-config.xml
Log does not contain segfault record
Collect Lua Coverage
Rspamd Teardown
Redis Teardown
+Wait For Rspamd Ports Released
+ [Documentation] Block until this suite's rspamd listening ports are
+ ... free, so the next suite on the same pabot worker can rebind them.
+ ... Checks the always-present normal + controller ports; each is given
+ ... up to ~6s (matches a slow worker shutdown under CPU contention) and
+ ... failure to free is a warning, not a hard error -- we don't want a
+ ... stuck port to mask the real test result, just to close the common
+ ... handoff race. See port_is_free in rspamd.py for why SO_REUSEADDR
+ ... does not cover this.
+ Run Keyword And Warn On Failure
+ ... Wait Until Keyword Succeeds 30x 0.2s
+ ... Port Is Free ${RSPAMD_LOCAL_ADDR} ${RSPAMD_PORT_NORMAL}
+ Run Keyword And Warn On Failure
+ ... Wait Until Keyword Succeeds 30x 0.2s
+ ... Port Is Free ${RSPAMD_LOCAL_ADDR} ${RSPAMD_PORT_CONTROLLER}
+
Run Redis
${RSPAMD_TMPDIR} = Make Temporary Directory
${template} = Get File ${RSPAMD_TESTDIR}/configs/redis-server.conf