]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Rewrite stress test to pytest
authorMichal Nowak <mnowak@isc.org>
Tue, 9 May 2023 17:11:00 +0000 (19:11 +0200)
committerMichal Nowak <mnowak@isc.org>
Tue, 8 Aug 2023 16:01:17 +0000 (18:01 +0200)
The shell version of the test was completed only after all DNS zone
updates were sent, even if the BIND server crashed while processing
them, leading to prolonged execution and potential hang in the CI
environment. The Python rewrite of the test ensures that DNS update
tasks finish within five minutes of starting, irrespective of a BIND
crash possibility or DNS zone updates not finishing in time.

(cherry picked from commit ecd7b30d0ac9f2ada45448f9adf7c9a22f33b122)

bin/tests/system/conf.sh.common
bin/tests/system/stress/clean.sh
bin/tests/system/stress/prereq.sh
bin/tests/system/stress/tests.sh [deleted file]
bin/tests/system/stress/tests_stress_update.py [new file with mode: 0644]
bin/tests/system/stress/update.pl [deleted file]

index c337f465dd955207d83abe3a57b0fd6ffd8156cc..e87acca6006aae41069c863b043792a1dde02a2c 100644 (file)
@@ -126,6 +126,7 @@ spf \
 staticstub \
 statistics \
 statschannel \
+stress \
 stub \
 synthfromdnssec \
 timeouts \
index b365d7cc77e83ec4271171d94804a56ad07244dc..0396845cbd81170b8ca391bb31058b38bbc78e59 100644 (file)
@@ -11,8 +11,6 @@
 # See the COPYRIGHT file distributed with this work for additional
 # information regarding copyright ownership.
 
-rm -f reload.pid
-
 rm -f ns?/zones.conf
 rm -f ns?/zone*.bk
 
index ec369f8586c81c44ee8c710d45b59b08f37853fb..aa97ae2448408f8e0a752d172b40e88b99b94694 100644 (file)
 SYSTEMTESTTOP=..
 . $SYSTEMTESTTOP/conf.sh
 
-if $PERL -e 'use Net::DNS;' 2>/dev/null
+if test -n "$PYTHON"
 then
-    :
+    if $PYTHON -c "import dns" 2> /dev/null
+    then
+        :
+    else
+        echo_i "This test requires the dnspython module." >&2
+        exit 1
+    fi
 else
-    echo_i "This test requires the Net::DNS library." >&2
+    echo_i "This test requires Python and the dnspython module." >&2
     exit 1
 fi
+
+exit 0
diff --git a/bin/tests/system/stress/tests.sh b/bin/tests/system/stress/tests.sh
deleted file mode 100644 (file)
index 9d36c6c..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/bin/sh
-
-# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
-#
-# SPDX-License-Identifier: MPL-2.0
-#
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0.  If a copy of the MPL was not distributed with this
-# file, you can obtain one at https://mozilla.org/MPL/2.0/.
-#
-# See the COPYRIGHT file distributed with this work for additional
-# information regarding copyright ownership.
-
-SYSTEMTESTTOP=..
-. $SYSTEMTESTTOP/conf.sh
-
-status=0
-
-(
-$SHELL -c "while true
-           do $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p $CONTROLPORT reload 2>&1 |
-              sed 's/^/I:ns3 /';
-          sleep 1
-       done" & echo $! >reload.pid
-) &
-
-for i in 0 1 2 3 4
-do
-       $PERL update.pl -s 10.53.0.2 -p $PORT zone00000$i.example. &
-done
-
-echo_i "waiting for background processes to finish"
-wait
-
-echo_i "killing reload loop"
-kill `cat reload.pid`
-
-# If the test has run to completion without named crashing, it has succeeded.
-# Otherwise, the crash will be detected by the test framework and the test will
-# fail.
-
-echo_i "exit status: $status"
-[ $status -eq 0 ] || exit 1
diff --git a/bin/tests/system/stress/tests_stress_update.py b/bin/tests/system/stress/tests_stress_update.py
new file mode 100644 (file)
index 0000000..638cda0
--- /dev/null
@@ -0,0 +1,77 @@
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# SPDX-License-Identifier: MPL-2.0
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0.  If a copy of the MPL was not distributed with this
+# file, you can obtain one at https://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+import concurrent.futures
+import os
+import subprocess
+import time
+import dns.query
+import dns.update
+
+
+def rndc_loop(test_state, server):
+    rndc = os.getenv("RNDC")
+    port = os.getenv("CONTROLPORT")
+
+    cmdline = [
+        rndc,
+        "-c",
+        "../common/rndc.conf",
+        "-p",
+        port,
+        "-s",
+        server,
+        "reload",
+    ]
+
+    while not test_state["finished"]:
+        subprocess.run(cmdline, check=False)
+        time.sleep(1)
+
+
+def update_zone(test_state, zone, named_port):
+    server = "10.53.0.2"
+    for i in range(1000):
+        if test_state["finished"]:
+            return
+        update = dns.update.UpdateMessage(zone)
+        update.add(f"dynamic-{i}.{zone}", 300, "TXT", f"txt-{i}")
+        try:
+            response = dns.query.udp(update, server, 10, named_port)
+            assert response.rcode() == dns.rcode.NOERROR
+        except dns.exception.Timeout:
+            print(f"error: query timeout for {zone}")
+
+    print(f"Update of {server} zone {zone} successful")
+
+
+# If the test has run to completion without named crashing, it has succeeded.
+def test_update_stress(named_port):
+    test_state = {"finished": False}
+
+    with concurrent.futures.ThreadPoolExecutor() as executor:
+        executor.submit(rndc_loop, test_state, "10.53.0.3")
+
+        updaters = []
+        for i in range(5):
+            zone = f"zone00000{i}.example."
+            updaters.append(executor.submit(update_zone, test_state, zone, named_port))
+
+        # All the update_zone() tasks are expected to complete within 5
+        # minutes.  If they do not, we cannot assert immediately as that will
+        # cause the ThreadPoolExecutor context manager to wait indefinitely;
+        # instead, we first signal all tasks that it is time to exit and only
+        # check whether any task failed to finish within 5 minutes outside of
+        # the ThreadPoolExecutor context manager.
+        unfinished_tasks = concurrent.futures.wait(updaters, timeout=300).not_done
+        test_state["finished"] = True
+
+    assert not unfinished_tasks
diff --git a/bin/tests/system/stress/update.pl b/bin/tests/system/stress/update.pl
deleted file mode 100644 (file)
index 1276b21..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-#!/usr/bin/perl
-
-# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
-#
-# SPDX-License-Identifier: MPL-2.0
-#
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0.  If a copy of the MPL was not distributed with this
-# file, you can obtain one at https://mozilla.org/MPL/2.0/.
-#
-# See the COPYRIGHT file distributed with this work for additional
-# information regarding copyright ownership.
-
-#
-# Dynamic update test suite.
-#
-# Usage:
-#
-#   perl update_test.pl [-s server] [-p port] zone
-#
-# The server defaults to 127.0.0.1.
-# The port defaults to 53.
-#
-# The "Special NS rules" tests will only work correctly if the
-# has no NS records to begin with, or alternatively has a
-# single NS record pointing at the name "ns1" (relative to
-# the zone name).
-#
-# Installation notes:
-#
-# This program uses the Net::DNS::Resolver module.
-# You can install it by saying
-#
-#    perl -MCPAN -e "install Net::DNS"
-#
-
-use Getopt::Std;
-use Net::DNS;
-use Net::DNS::Update;
-use Net::DNS::Resolver;
-
-$opt_s = "127.0.0.1";
-$opt_p = 53;
-
-getopt('s:p:');
-
-$res = new Net::DNS::Resolver;
-$res->nameservers($opt_s);
-$res->port($opt_p);
-$res->defnames(0); # Do not append default domain.
-
-@ARGV == 1 or die
-    "usage: perl update_test.pl [-s server] [-p port] zone\n";
-
-$zone = shift @ARGV;
-
-my $failures = 0;
-
-sub assert {
-    my ($cond, $explanation) = @_;
-    if (!$cond) {
-       print "I:Test Failed: $explanation ***\n";
-       $failures++
-    }
-}
-
-sub test {
-    my ($expected, @records) = @_;
-
-    my $update = new Net::DNS::Update("$zone");
-
-    foreach $rec (@records) {
-       $update->push(@$rec);
-    }
-
-    $reply = $res->send($update);
-
-    # Did it work?
-    if (defined $reply) {
-       my $rcode = $reply->header->rcode;
-        assert($rcode eq $expected, "expected $expected, got $rcode");
-    } else {
-       print "I:Update failed: ", $res->errorstring, "\n";
-    }
-}
-
-sub section {
-    my ($msg) = @_;
-    print "I:$msg\n";
-}
-
-for ($i = 0; $i < 1000; $i++) {
-    test("NOERROR", ["update", rr_add("dynamic-$i.$zone 300 TXT txt-$i" )]);
-}
-
-if ($failures) {
-    print "I:$failures tests failed.\n";
-} else {
-    print "I:Update of $opt_s zone $zone successful.\n";
-}
-exit $failures;