summaryrefslogtreecommitdiffstats
path: root/bin/tests/system/shutdown
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--bin/tests/system/shutdown/clean.sh20
-rw-r--r--bin/tests/system/shutdown/ns1/named.conf.in42
-rw-r--r--bin/tests/system/shutdown/ns1/root.db25
-rw-r--r--bin/tests/system/shutdown/ns2/named.conf.in40
-rw-r--r--bin/tests/system/shutdown/ns2/test.db18
-rw-r--r--bin/tests/system/shutdown/resolver/named.conf.in48
-rw-r--r--bin/tests/system/shutdown/resolver/root.db21
-rw-r--r--bin/tests/system/shutdown/setup.sh22
-rwxr-xr-xbin/tests/system/shutdown/tests_shutdown.py206
9 files changed, 442 insertions, 0 deletions
diff --git a/bin/tests/system/shutdown/clean.sh b/bin/tests/system/shutdown/clean.sh
new file mode 100644
index 0000000..d958521
--- /dev/null
+++ b/bin/tests/system/shutdown/clean.sh
@@ -0,0 +1,20 @@
+#!/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.
+
+rm -f ns*/*.jnl
+rm -f ns*/named.lock
+rm -f ns*/named.memstats
+rm -f ns*/rpz*.txt
+rm -f */named.conf
+rm -f */named.run
+rm -rf __pycache__
diff --git a/bin/tests/system/shutdown/ns1/named.conf.in b/bin/tests/system/shutdown/ns1/named.conf.in
new file mode 100644
index 0000000..f0f5210
--- /dev/null
+++ b/bin/tests/system/shutdown/ns1/named.conf.in
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+key rndc_key {
+ secret "1234abcd8765";
+ algorithm @DEFAULT_HMAC@;
+};
+
+controls {
+ inet 10.53.0.1 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
+};
+
+options {
+ query-source address 10.53.0.1;
+ notify-source 10.53.0.1;
+ transfer-source 10.53.0.1;
+ port @PORT@;
+ listen-on { 10.53.0.1; };
+ pid-file "named.pid";
+ notify no;
+ dnssec-validation no;
+ allow-query { any; };
+ recursion yes;
+ allow-recursion { any; };
+};
+
+# Delegate .test domain to 10.53.0.2
+zone "." {
+ type primary;
+ file "root.db";
+ allow-transfer { none; };
+};
diff --git a/bin/tests/system/shutdown/ns1/root.db b/bin/tests/system/shutdown/ns1/root.db
new file mode 100644
index 0000000..60f1b30
--- /dev/null
+++ b/bin/tests/system/shutdown/ns1/root.db
@@ -0,0 +1,25 @@
+; 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.
+
+$TTL 300
+@ IN SOA a.root. root.test. (
+ 2000042100 ; serial
+ 600 ; refresh
+ 600 ; retry
+ 1200 ; expire
+ 600 ; minimum
+ )
+
+. IN NS a.root.
+a.root. IN A 10.53.0.1
+
+test IN NS ns1.test
+ns1.test IN A 10.53.0.2
diff --git a/bin/tests/system/shutdown/ns2/named.conf.in b/bin/tests/system/shutdown/ns2/named.conf.in
new file mode 100644
index 0000000..2ed91dc
--- /dev/null
+++ b/bin/tests/system/shutdown/ns2/named.conf.in
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+key rndc_key {
+ secret "1234abcd8765";
+ algorithm @DEFAULT_HMAC@;
+};
+
+controls {
+ inet 10.53.0.1 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
+};
+
+options {
+ query-source address 10.53.0.2;
+ notify-source 10.53.0.2;
+ transfer-source 10.53.0.2;
+ port @PORT@;
+ listen-on { 10.53.0.2; };
+ pid-file "named.pid";
+ notify no;
+ dnssec-validation no;
+ allow-query { any; };
+};
+
+# 10.53.0.2 is authoritative for .test domain
+zone "test" {
+ type primary;
+ file "test.db";
+ allow-transfer { none; };
+};
diff --git a/bin/tests/system/shutdown/ns2/test.db b/bin/tests/system/shutdown/ns2/test.db
new file mode 100644
index 0000000..91c16ec
--- /dev/null
+++ b/bin/tests/system/shutdown/ns2/test.db
@@ -0,0 +1,18 @@
+; 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.
+
+$TTL 300
+
+@ IN SOA ns1 root.test. 2020040101 4h 1h 1w 60
+@ IN NS ns1
+ns1 IN A 10.53.0.2
+@ IN A 10.53.0.2
+www IN A 10.53.0.2
diff --git a/bin/tests/system/shutdown/resolver/named.conf.in b/bin/tests/system/shutdown/resolver/named.conf.in
new file mode 100644
index 0000000..5007579
--- /dev/null
+++ b/bin/tests/system/shutdown/resolver/named.conf.in
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+logging {
+ channel basic {
+ file "named.run";
+ severity debug 999;
+ print-time yes;
+ };
+ category default { basic; };
+};
+
+key rndc_key {
+ secret "1234abcd8765";
+ algorithm @DEFAULT_HMAC@;
+};
+
+controls {
+ inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
+};
+
+options {
+ query-source address 10.53.0.3;
+ notify-source 10.53.0.3;
+ transfer-source 10.53.0.3;
+ port @PORT@;
+ listen-on { 10.53.0.3; };
+ pid-file "named.pid";
+ notify no;
+ dnssec-validation no;
+ allow-query { any; };
+ allow-recursion { any; };
+};
+
+zone "." {
+ type hint;
+ file "root.db";
+};
diff --git a/bin/tests/system/shutdown/resolver/root.db b/bin/tests/system/shutdown/resolver/root.db
new file mode 100644
index 0000000..88e0ba8
--- /dev/null
+++ b/bin/tests/system/shutdown/resolver/root.db
@@ -0,0 +1,21 @@
+; 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.
+
+$TTL 300
+. IN SOA a.root. root.root. (
+ 2000042100 ; serial
+ 600 ; refresh
+ 600 ; retry
+ 1200 ; expire
+ 600 ; minimum
+ )
+. IN NS a.root.
+a.root. IN A 10.53.0.1
diff --git a/bin/tests/system/shutdown/setup.sh b/bin/tests/system/shutdown/setup.sh
new file mode 100644
index 0000000..cdeab82
--- /dev/null
+++ b/bin/tests/system/shutdown/setup.sh
@@ -0,0 +1,22 @@
+#!/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.
+
+# touch dnsrps-off to not test with DNSRPS
+
+set -e
+
+. ../conf.sh
+
+copy_setports ns1/named.conf.in ns1/named.conf
+copy_setports ns2/named.conf.in ns2/named.conf
+copy_setports resolver/named.conf.in resolver/named.conf
diff --git a/bin/tests/system/shutdown/tests_shutdown.py b/bin/tests/system/shutdown/tests_shutdown.py
new file mode 100755
index 0000000..89cada2
--- /dev/null
+++ b/bin/tests/system/shutdown/tests_shutdown.py
@@ -0,0 +1,206 @@
+#!/usr/bin/python3
+
+# 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.
+
+from concurrent.futures import ThreadPoolExecutor, as_completed
+import os
+import random
+import signal
+import subprocess
+from string import ascii_lowercase as letters
+import time
+
+import pytest
+
+pytest.importorskip("dns", minversion="2.0.0")
+import dns.exception
+import dns.resolver
+
+
+def do_work(named_proc, resolver, rndc_cmd, kill_method, n_workers, n_queries):
+ """Creates a number of A queries to run in parallel
+ in order simulate a slightly more realistic test scenario.
+
+ The main idea of this function is to create and send a bunch
+ of A queries to a target named instance and during this process
+ a request for shutting down named will be issued.
+
+ In the process of shutting down named, a couple control connections
+ are created (by launching rndc) to ensure that the crash was fixed.
+
+ if kill_method=="rndc" named will be asked to shutdown by
+ means of rndc stop.
+ if kill_method=="sigterm" named will be killed by SIGTERM on
+ POSIX systems or by TerminateProcess() on Windows systems.
+
+ :param named_proc: named process instance
+ :type named_proc: subprocess.Popen
+
+ :param resolver: target resolver
+ :type resolver: dns.resolver.Resolver
+
+ :param rndc_cmd: rndc command with default arguments
+ :type rndc_cmd: list of strings, e.g. ["rndc", "-p", "23750"]
+
+ :kill_method: "rndc" or "sigterm"
+ :type kill_method: str
+
+ :param n_workers: Number of worker threads to create
+ :type n_workers: int
+
+ :param n_queries: Total number of queries to send
+ :type n_queries: int
+ """
+ # pylint: disable-msg=too-many-arguments
+ # pylint: disable-msg=too-many-locals
+
+ # helper function, args must be a list or tuple with arguments to rndc.
+ def launch_rndc(args):
+ return subprocess.call(rndc_cmd + args, timeout=10)
+
+ # We're going to execute queries in parallel by means of a thread pool.
+ # dnspython functions block, so we need to circunvent that.
+ with ThreadPoolExecutor(n_workers + 1) as executor:
+ # Helper dict, where keys=Future objects and values are tags used
+ # to process results later.
+ futures = {}
+
+ # 50% of work will be A queries.
+ # 1 work will be rndc stop.
+ # Remaining work will be rndc status (so we test parallel control
+ # connections that were crashing named).
+ shutdown = True
+ for i in range(n_queries):
+ if i < (n_queries // 2):
+ # Half work will be standard A queries.
+ # Among those we split 50% queries relname='www',
+ # 50% queries relname=random characters
+ if random.randrange(2) == 1:
+ tag = "good"
+ relname = "www"
+ else:
+ tag = "bad"
+ length = random.randint(4, 10)
+ relname = "".join(
+ letters[random.randrange(len(letters))] for i in range(length)
+ )
+
+ qname = relname + ".test"
+ futures[executor.submit(resolver.resolve, qname, "A")] = tag
+ elif shutdown: # We attempt to stop named in the middle
+ shutdown = False
+ if kill_method == "rndc":
+ futures[executor.submit(launch_rndc, ["stop"])] = "stop"
+ else:
+ futures[executor.submit(named_proc.terminate)] = "kill"
+ else:
+ # We attempt to send couple rndc commands while named is
+ # being shutdown
+ futures[executor.submit(launch_rndc, ["status"])] = "status"
+
+ ret_code = -1
+ for future in as_completed(futures):
+ try:
+ result = future.result()
+ # If tag is "stop", result is an instance of
+ # subprocess.CompletedProcess, then we check returncode
+ # attribute to know if rncd stop command finished successfully.
+ #
+ # if tag is "kill" then the main function will check if
+ # named process exited gracefully after SIGTERM signal.
+ if futures[future] == "stop":
+ ret_code = result
+
+ except (
+ dns.resolver.NXDOMAIN,
+ dns.resolver.NoNameservers,
+ dns.exception.Timeout,
+ ):
+ pass
+
+ if kill_method == "rndc":
+ assert ret_code == 0
+
+
+def wait_for_named_loaded(resolver, retries=10):
+ for _ in range(retries):
+ try:
+ resolver.resolve("version.bind", "TXT", "CH")
+ return True
+ except (dns.resolver.NoNameservers, dns.exception.Timeout):
+ time.sleep(1)
+ return False
+
+
+def wait_for_proc_termination(proc, max_timeout=10):
+ for _ in range(max_timeout):
+ if proc.poll() is not None:
+ return True
+ time.sleep(1)
+
+ proc.send_signal(signal.SIGABRT)
+ for _ in range(max_timeout):
+ if proc.poll() is not None:
+ return True
+ time.sleep(1)
+
+ return False
+
+
+# We test named shutting down using two methods:
+# Method 1: using rndc ctop
+# Method 2: killing with SIGTERM
+# In both methods named should exit gracefully.
+@pytest.mark.parametrize("kill_method", ["rndc", "sigterm"])
+def test_named_shutdown(named_port, control_port, kill_method):
+ # pylint: disable-msg=too-many-locals
+ cfg_dir = os.path.join(os.getcwd(), "resolver")
+ assert os.path.isdir(cfg_dir)
+
+ cfg_file = os.path.join(cfg_dir, "named.conf")
+ assert os.path.isfile(cfg_file)
+
+ named = os.getenv("NAMED")
+ assert named is not None
+
+ rndc = os.getenv("RNDC")
+ assert rndc is not None
+
+ # rndc configuration resides in ../common/rndc.conf
+ rndc_cfg = os.path.join("..", "common", "rndc.conf")
+ assert os.path.isfile(rndc_cfg)
+
+ # rndc command with default arguments.
+ rndc_cmd = [rndc, "-c", rndc_cfg, "-p", str(control_port), "-s", "10.53.0.3"]
+
+ # We create a resolver instance that will be used to send queries.
+ resolver = dns.resolver.Resolver()
+ resolver.nameservers = ["10.53.0.3"]
+ resolver.port = named_port
+
+ named_cmdline = [named, "-c", cfg_file, "-f"]
+ with subprocess.Popen(named_cmdline, cwd=cfg_dir) as named_proc:
+ try:
+ assert named_proc.poll() is None, "named isn't running"
+ assert wait_for_named_loaded(resolver)
+ do_work(
+ named_proc,
+ resolver,
+ rndc_cmd,
+ kill_method,
+ n_workers=12,
+ n_queries=16,
+ )
+ assert wait_for_proc_termination(named_proc)
+ assert named_proc.returncode == 0, "named crashed"
+ finally: # Ensure named is terminated in case of an exception
+ named_proc.kill()