Commit d6a39f5b authored by Tomas Krizek's avatar Tomas Krizek Committed by Petr Špaček

job_manager/create: tool to create respdiff jobs

parent 6a3391d5
#!/usr/bin/env python3
import argparse
import glob
import logging
import os
import shutil
import stat
import sys
import traceback
from typing import Any, Dict, List, Mapping
import jinja2
import yaml
DIR_PATH = os.path.dirname(os.path.realpath(__file__))
TEST_CASE_DIR = os.path.join(DIR_PATH, 'test_cases')
FILES_DIR = os.path.join(DIR_PATH, 'files')
def prepare_dir(directory: str, clean: bool = False) -> None:
if clean:
try:
shutil.rmtree(directory)
except FileNotFoundError:
pass
try:
os.makedirs(directory)
except FileExistsError:
raise RuntimeError(
'Directory "{}" already exists! Use -l label / --clean or (re)move the '
'directory manually to resolve the issue.'.format(directory))
def copy_file(name: str, destdir: str, destname: str = ''):
if not destname:
destname = name
shutil.copy(
os.path.join(FILES_DIR, name),
os.path.join(destdir, destname))
def create_file_from_template(
name: str,
data: Mapping[str, Any],
destdir: str,
destname: str = '',
executable=False
) -> None:
env = jinja2.Environment(loader=jinja2.FileSystemLoader(FILES_DIR))
template = env.get_template(name)
rendered = template.render(**data)
if not destname:
assert name[-3:] == '.j2'
destname = os.path.basename(name)[:-3]
dest = os.path.join(destdir, destname)
with open(dest, 'w') as fh:
fh.write(rendered)
if executable:
st = os.stat(dest)
os.chmod(dest, st.st_mode | stat.S_IEXEC)
def load_test_case_config(test_case: str) -> Dict[str, Any]:
path = os.path.join(TEST_CASE_DIR, test_case + '.yaml')
with open(path, 'r') as f:
return yaml.load(f)
def create_template_files(directory: str, config: Dict[str, Any]):
create_file_from_template('run_respdiff.sh.j2', config, directory, executable=True)
create_file_from_template('restart-all.sh.j2', config, directory, executable=True)
create_file_from_template('docker-compose.yaml.j2', config, directory)
for name, resolver in config['resolvers'].items():
resolver['name'] = name
if resolver['type'] == 'knot-resolver':
dockerfile_dir = os.path.join(directory, 'docker-knot-resolver')
if not os.path.exists(dockerfile_dir):
os.makedirs(dockerfile_dir)
copy_file('Dockerfile.knot-resolver', dockerfile_dir, 'Dockerfile')
create_file_from_template(
'kresd.conf.j2', resolver, directory, name + '.conf')
copy_file('root.keys', directory)
elif resolver['type'] == 'unbound':
create_file_from_template(
'unbound.conf.j2', resolver, directory, name + '.conf')
copy_file('cert.pem', directory)
copy_file('key.pem', directory)
copy_file('root.keys', directory)
elif resolver['type'] == 'bind':
create_file_from_template(
'named.conf.j2', resolver, directory, name + '.conf')
copy_file('rfc1912.zones', directory)
copy_file('bind.keys', directory)
else:
raise NotImplementedError(
"unknown resolver type: '{}'".format(resolver['type']))
# omit resolvers without respdiff section from respdiff.cfg
config['resolvers'] = {
name: res for name, res
in config['resolvers'].items()
if 'respdiff' in res}
create_file_from_template('respdiff.cfg.j2', config, directory)
def get_test_case_list(nameglob: str = '') -> List[str]:
return [
os.path.splitext(os.path.basename(fname))[0]
for fname in glob.glob(os.path.join(TEST_CASE_DIR, '{}*.yaml'.format(nameglob)))]
def create_jobs(args: argparse.Namespace) -> None:
test_cases = [] # type: List[str]
if args.t is not None:
test_cases.append(args.t)
else:
test_cases = get_test_case_list(args.all)
if not test_cases:
raise RuntimeError("No test cases found!")
git_sha = args.sha_or_tag[:8]
commit_dir = git_sha
if args.label is not None:
if ' ' in args.label:
raise RuntimeError('Label may not contain spaces.')
commit_dir += '-' + args.label
for test_case in test_cases:
config = load_test_case_config(test_case)
config['git_sha'] = git_sha
directory = os.path.join(args.jobs_dir, commit_dir, test_case)
prepare_dir(directory, clean=args.clean)
create_template_files(directory, config)
# print out created directory so it can be supplied as argument(s)
# for submit.py
print(directory)
def main() -> None:
parser = argparse.ArgumentParser(
description="Prepare files for docker respdiff job")
parser.add_argument(
'sha_or_tag', type=str,
help="Knot Resolver git commit or tag to use (don't use branch!)")
parser.add_argument(
'-a', '--all', default='shortlist',
help="Create all test cases which start with expression (default: shortlist)")
parser.add_argument(
'-t', choices=get_test_case_list(),
help="Create only the specified test case")
parser.add_argument(
'-l', '--label',
help="Assign label for easier job identification and isolation")
parser.add_argument(
'--clean', action='store_true',
help="Remove target directory if it already exists (use with caution!)")
parser.add_argument(
'--jobs-dir', default='/var/tmp/respdiff-jobs',
help="Directory with job collections (default: /var/tmp/respdiff-jobs)")
args = parser.parse_args()
create_jobs(args)
if __name__ == '__main__':
logging.basicConfig(
format='%(asctime)s %(levelname)8s %(message)s', level=logging.DEBUG)
try:
main()
except RuntimeError as exc:
logging.debug(traceback.format_exc())
logging.error(str(exc))
sys.exit(1)
except Exception as exc:
logging.debug(traceback.format_exc())
logging.critical('Unhandled code exception: %s', str(exc))
sys.exit(2)
../../docker/knot-resolver/Dockerfile
\ No newline at end of file
# The bind.keys file is used to override the built-in DNSSEC trust anchors
# which are included as part of BIND 9. The only trust anchors it contains
# are for the DNS root zone ("."). Trust anchors for any other zones MUST
# be configured elsewhere; if they are configured here, they will not be
# recognized or used by named.
#
# The built-in trust anchors are provided for convenience of configuration.
# They are not activated within named.conf unless specifically switched on.
# To use the built-in key, use "dnssec-validation auto;" in the
# named.conf options. Without this option being set, the keys in this
# file are ignored.
#
# This file is NOT expected to be user-configured.
#
# These keys are current as of October 2017. If any key fails to
# initialize correctly, it may have expired. In that event you should
# replace this file with a current version. The latest version of
# bind.keys can always be obtained from ISC at https://www.isc.org/bind-keys.
#
# See https://data.iana.org/root-anchors/root-anchors.xml
# for current trust anchor information for the root zone.
managed-keys {
# This key (19036) is to be phased out starting in 2017. It will
# remain in the root zone for some time after its successor key
# has been added. It will remain this file until it is removed from
# the root zone.
. initial-key 257 3 8 "AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjF
FVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoX
bfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaD
X6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpz
W5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relS
Qageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulq
QxA+Uk1ihz0=";
# This key (20326) was published in the root zone in 2017.
# Servers which were already using the old key (19036) should
# roll seamlessly to this new one via RFC 5011 rollover. Servers
# being set up for the first time can use the contents of this
# file as initializing keys; thereafter, the keys in the
# managed key database will be trusted and maintained
# automatically.
. initial-key 257 3 8 "AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3
+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kv
ArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF
0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+e
oZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfd
RUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwN
R1AkUTV74bU=";
};
-----BEGIN CERTIFICATE-----
MIIFXTCCA0WgAwIBAgIJAOgYHvStVrNoMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTcxMTA5MTI0MzU5WhcNMTgxMTA5MTI0MzU5WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
CgKCAgEAwKpSN2PtS3PvHeN1ePj/WgnperIgPUo035/K2H+rS7l5AJavyUrZz4HC
l5ywH1JZaWxf3EhYiiMp/lEhrQ1+ByJhwSZ2f0IautDTITZvc8S+CTiG26OANwrj
R+V9a5AAxKv+JZbPAk3sQacJXaT8t5EtsVWKmRodd4tZh1Pqle4LTj8N7Z4GXrVX
ueFgrWnBLZRnvEJ+uk7KA7lzXLDg6XqYM0KVL9Z8nqyEveYXNOCwqANmhapMOwgq
rQfwCoRVkAf9RgL5pYrbYYh3ccTRi1SC0aBc5fdrHvpdEvhCWb7SDCzZUfNDq/oC
uM3NlFtqVK7rulurbxt397OVHlvY2bmTCA2heb8T7dt3xtvrUVU0Wtu/I+SBDqkc
V6WEuKTsg50vMKbleWRLjuHDZUcNGm75Ztqktz2Ln1BF8FNEC62jx3Rq6Im17u+w
Gl+TMeLjhAqLDWPcRntxoCaUudAN9aqB+Ur3+WqH6MAVN5OV8r65pbBy9qYklJ3T
3KBgNa3btfhXm0A1ZTw2kaOmTmZuBUBBVtPy7otv6Ki57blGGUjEPwFDcmlNfilh
S/QrR9CH6zREAhqLgz9Qt7ZKBltiLBQYqOozdLoYh3EgUXecwpKNWpxDvgAwJvQU
qVnuIuKcQ978tcij/X1KoKbaJ0yw2t2nZylwmFu7mdEdEMQ8dYcCAwEAAaNQME4w
HQYDVR0OBBYEFBp9sHuFvJhesF4ZOmLxsrUtJR1KMB8GA1UdIwQYMBaAFBp9sHuF
vJhesF4ZOmLxsrUtJR1KMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
AE4bfFLenxSLvn0fuv6PR5tKxfoKSIB9urN3d/CrnvrO+RQzvdpbP83xTQlSUWR1
cHq5W8yNOYFtigocPo4xwo2xVG982fRzPXziS74EA4lT5sNFUf1BWBcyXBPkbKwc
gI84OmAKJR0THyBQWD9rU8J7S7QgWgYwXlcqF7Ssdb4vGQ/Z6NZYMk6VXgE7fwX/
EvQHGf7nBPXRZxJ5BTJeTkoXp9g8RwUzA+tPbiEjEYLYbLt/n4Gr/L1Bd0JH3kji
SVlkhptTfBEa8agwTiRbkO97PXWLUuqxI7NvyjcibpjSbosdvW7tpmbBoGmtykk7
rJGKDGrAnUr7v1gVeOov8Qc3d2nbROu7Haa7vuGFST4h7D+ofOjj+TLiiCMxf3cp
CBrUj4MVeAXZKepFHvKN1t2mHK4KEKM0dgCNAozHWnkNcbNIZlXuGWt8gmkmNAYp
9Al9+0pvY6ua6pPJsOqPRhB9HHeg4mLiuNBnuyl9OCGBn6Zj1ABdry9hbiEW4SJi
/SO0YgU1sIAvft2VfbJZdh7SuRa//WokUFOyjamXcEBr2zJIUYpUUkoRoxXEcFoZ
zpiEkqh6odHFVzl1Rvoez8jeqqJRwdNxGs0fJUVqFsHi7leMqOipUY4LmOclKSmZ
gGx5fl0KNeb7M4KffwHSqAU0hHstJ9cSTyhvrFaX7EKF
-----END CERTIFICATE-----
version: '3.2'
services:
{%- for name, resolver in resolvers.items() %}
{{ name }}:
network_mode: host
{%- if resolver['type'] == 'knot-resolver' %}
build:
context: ./docker-knot-resolver
args:
GIT_SHA: {{ git_sha }}
image: knot-resolver:{{ git_sha }}
volumes:
- "./{{ name }}.conf:/etc/knot-resolver/kresd.conf:ro"
- "./root.keys:/etc/knot-resolver/root.keys:ro"
- type: tmpfs
target: /dev/shm
{%- endif -%}
{%- if resolver['type'] == 'bind' %}
image: registry.labs.nic.cz/knot/respdiff/bind:latest
volumes:
- "./{{ name }}.conf:/etc/bind9/named.conf:ro"
- "./rfc1912.zones:/etc/bind9/rfc1912.zones:ro"
- "./bind.keys:/etc/bind9/bind.keys:ro"
{%- endif -%}
{%- if resolver['type'] == 'unbound' %}
image: registry.labs.nic.cz/knot/respdiff/unbound:latest
volumes:
- "./{{ name }}.conf:/usr/local/etc/unbound/unbound.conf:ro"
- "./root.keys:/usr/local/etc/unbound/root.keys:ro"
- "./cert.pem:/usr/local/etc/unbound/cert.pem:ro"
- "./key.pem:/usr/local/etc/unbound/key.pem:ro"
{%- endif -%}
{%- endfor -%}
-----BEGIN PRIVATE KEY-----
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
lqJHs10TgmiYxGUFXzbVGJv/nyZObMU8/u58fQIUnQKCAQBS+PQ6cHuw68P/VJin
4hpAwRGSEEaPErU8TnbvHUA8zxgSBQoZkLrvaWYvvwA5BeXE2yBujFA2pZRIA8sZ
KLrXQQ5q2xUGQbDKmpLEg2ivoTVrzIlNxc2BrbYbC3uYV6fD/r1Y2zAMNwH4MCP9
U77fitbNBRgkZaCj3uh2At2/cuu39QjtWy8szve7Lv5zV0vW7oSVuA6pHtoq3k5v
+/BkUZXZYD0LEZfGFF/qXsBnTmWEZlkLOEYVwNy5GkL0LJsOjMFRGlNeKfqqje1z
+w3DV2dpt9wnouEEKQOgnBz0b5ZNxCd+KzIQTI3AubeVIXoI/txImrZ1YmmU+7m+
hhthAoIBAQCt27uh6L5hPg2wGBRoq0kMJre/y3q8emyvDzG1o+XTPTS/VnwqPejH
aUBSoozf1IkhazZVvC0VOBntA3QnIvB3uhTlMNIbK1mO81YK89Xc+X4NqeIneNJo
bNpaIJqy+2UY0DT48kbjyZSz1Yt1EbjrHCTycmPj5u1TQNkMz9ve87z+FBfvoGel
R1dILMnzvUmu3o2OUP+PiGQqaFXvd3SfPXS6GakXhFXuhjj+oONDCYJtchJ/gkiG
MphFZUKtYD4hc8D0GkSYiYQ+XC7EVMRVoDFcSuTN4Of9tVJCeVB9rC5C4wGfoQgU
qMNU3k+fHVcq8Bg9fvg/y19RJ/dUDUqI
-----END PRIVATE KEY-----
-- Refer to manual: https://knot-resolver.readthedocs.io/en/latest/daemon.html#configuration
-- Configure interfaces
net.listen('127.0.0.1', {{ port }})
{% if listen_ipv6 -%}
net.listen('::1', {{ port }})
{%- endif %}
{% if tls_port %}
net.listen('127.0.0.1', {{ tls_port }}, {tls = true})
{% if listen_ipv6 -%}
net.listen('::1', {{ tls_port }}, {tls = true})
{%- endif %}
{% endif %}
-- essential for TLS_FORWARD in Docker with lots of respdiff jobs
net.tcp_pipeline(65535)
{% if forward %}
-- forwarding
policy.add(policy.all(
{% if 'tls' in forward and forward['tls'] %}
policy.TLS_FORWARD({
{"{{ forward['ip'] }}@{{ forward['port'] }}", insecure=true}})
{% else %}
policy.FORWARD("{{ forward['ip'] }}@{{ forward['port'] }}")
{% endif %}
))
{% endif %}
-- Large cache size, so we don't need to flush often
-- This can be larger than available RAM, least frequently accessed
-- records will be paged out
cache.size = 1000 * MB
verbose(false)
-- Load Useful modules
modules = {
'workarounds < iterate',
'policy', -- Block queries to local zones/bad sites
'view', -- Views for certain clients
'stats', -- Track internal statistics
}
options {
listen-on port {{ port }} { 127.0.0.1; };
{% if listen_ipv6 -%}
listen-on-v6 port {{ port }} { ::1; };
{% else %}
listen-on-v6 {none;};
{%- endif %}
directory "/dev/shm";
dump-file "/dev/shm/cache_dump.db";
statistics-file "/dev/shm/named_stats.txt";
memstatistics-file "/dev/shm/named_mem_stats.txt";
allow-query { localhost; };
max-cache-size 1048576000;
resolver-query-timeout 4;
recursion yes;
dnssec-enable yes;
dnssec-validation yes;
empty-zones-enable yes;
managed-keys-directory "/dev/shm";
pid-file "/dev/shm/named.pid";
session-keyfile "/dev/shm/session.key";
};
{% if forward %}
zone "." IN {
type forward;
forward only;
forwarders { {{ forward['ip'] }} port {{ forward['port'] }}; };
};
{% endif %}
include "/etc/bind9/rfc1912.zones";
include "/etc/bind9/bind.keys";
[sendrecv]
# in seconds
timeout = 5
# number of queries to run simultaneously
jobs = {{ respdiff['jobs'] }}
# in seconds (float); delay each query by a random time (uniformly distributed) between min and max; set max to 0 to disable
time_delay_min = 0
time_delay_max = 0
[servers]
names = {{ resolvers | join(', ') }}
# symbolic names of DNS servers under test
# separate multiple values by ,
# each symbolic name in [servers] section refers to config section
# containing IP address and port of particular server
{%- for resolver in resolvers.values() %}
[{{ resolver['name'] }}]
ip = {{ resolver['respdiff']['ip'] }}
port = {{ resolver['respdiff']['port'] }}
transport = {{ resolver['respdiff']['transport'] }}
{% if 'graph_color' in resolver['respdiff'] -%}
graph_color = {{ resolver['respdiff']['graph_color'] }}
{%- endif %}
{% if 'restart_script' in resolver['respdiff'] -%}
restart_script = {{ resolver['respdiff']['restart_script'] }}
{%- endif %}
{% endfor %}
[diff]
# symbolic name of server under test
# other servers are used as reference when comparing answers from the target
target = {{ respdiff['target'] }}
# fields and comparison methods used when comparing two DNS messages
criteria = opcode, rcode, flags, question, qname, qtype, answertypes, answerrrsigs
# other supported criteria values: authority, additional, edns, nsid
[report]
# diffsum reports mismatches in field values in this order
# if particular message has multiple mismatches, it is counted only once into category with highest weight
field_weights = timeout, malformed, opcode, qcase, qtype, rcode, flags, answertypes, answerrrsigs, answer, authority, additional, edns, nsid
#!/bin/bash
set -o errexit -o nounset
# How long to wait until resolvers bind to their port after restart
# 50 = 5s
MAX_RETRIES=50
docker-compose kill &>/dev/null || :
docker-compose up -d &>/dev/null
{% for resolver in resolvers.values() -%}
retries=0
until $(nc -z 127.0.0.1 {{ resolver['port'] }}); do
retries=$((retries+1))
if (( $retries > $MAX_RETRIES )); then
exit 1
fi
sleep 0.1
done
{% endfor %}
// named.rfc1912.zones:
//
// Provided by Red Hat caching-nameserver package
//
// ISC BIND named zone configuration for zones recommended by
// RFC 1912 section 4.1 : localhost TLDs and address zones
// and http://www.ietf.org/internet-drafts/draft-ietf-dnsop-default-local-zones-02.txt
// (c)2007 R W Franks
//
// See /usr/share/doc/bind*/sample/ for example named configuration files.
//
zone "localhost.localdomain" IN {
type master;
file "named.localhost";
allow-update { none; };
};
zone "localhost" IN {
type master;
file "named.localhost";
allow-update { none; };
};
zone "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa" IN {
type master;
file "named.loopback";
allow-update { none; };
};
zone "1.0.0.127.in-addr.arpa" IN {
type master;
file "named.loopback";
allow-update { none; };
};
zone "0.in-addr.arpa" IN {
type master;
file "named.empty";
allow-update { none; };
};
. 172800 DNSKEY 257 3 8 AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjFFVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoXbfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaDX6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpzW5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relSQageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulqQxA+Uk1ihz0= ; Valid: ; KeyTag:19036
. 172800 DNSKEY 257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ; Valid: ; KeyTag:20326
#!/bin/bash
set -o xtrace
JOBNAME=j$1.$2
# HACK (condor): ensure files for transfer exist to avoid held jobs
touch ${JOBNAME}_report.json
touch ${JOBNAME}_report.txt
touch ${JOBNAME}_histogram.svg
# HACK (condor): Create proper dir structure
mkdir -p docker-knot-resolver || :
mv Dockerfile docker-knot-resolver/ || :
# Check no other docker containers are running
if [ -n "$(docker ps -q)" ]; then
echo "ERROR: running docker containers detected" >&2
exit 2
fi
# Ensure ports are empty, so we're not testing something else!
MAX_RETRIES=60
{% for resolver in resolvers.values() -%}
retries=0
{% if resolver['port'] %}
while [ ! -z $(ss --all --no-header '( dport = :{{ resolver["port"] }} or sport = :{{ resolver["port"] }} )') ]; do
retries=$((retries+1))
if (( $retries > $MAX_RETRIES )); then
echo "ERROR: port {{ resolver['port'] }} not availble" >&2
exit 2
fi
sleep 1
done
{% endif %}
{% if resolver['tls_port'] %}
retries=0
while [ ! -z $(ss --all --no-header '( dport = :{{ resolver["tls_port"] }} or sport = :{{ resolver["tls_port"] }} )') ]; do
retries=$((retries+1))
if (( $retries > $MAX_RETRIES )); then
echo "ERROR: port {{ resolver['tls_port'] }} not availble" >&2
exit 2
fi
sleep 1
done
{% endif %}
{% endfor %}
# Exit if any cmd fails from now on.
set -o errexit
cleanup() {
docker-compose rm -fsv || :
{%if database['remove_after'] %}
rm -rf ./*.mdb || :
{% endif %}
exit 0
}
trap cleanup ERR EXIT
# Get database
wget --quiet -O {{ database['dest'] }} {{ database['url'] }}
# Attempt to build containers (to have stdout/err logs in case of failure)
time docker-compose up --no-start
# Start and wait for resolvers to be available
./restart-all.sh
# Run respdiff toolchain
time "{{ respdiff['srcdir'] }}/orchestrator.py" .
docker-compose stop # give lingering connections time to close
time "{{ respdiff['srcdir'] }}/msgdiff.py" -d ${JOBNAME}_report.json .
time "{{ respdiff['srcdir'] }}/diffrepro.py" -d ${JOBNAME}_report.json .
time "{{ respdiff['srcdir'] }}/diffsum.py" -d ${JOBNAME}_report.json . > ${JOBNAME}_report.txt
time "{{ respdiff['srcdir'] }}/histogram.py" -o ${JOBNAME}_histogram.svg .
Executable = run_respdiff.sh
Error = j$(Cluster).$(Process)_stderr
Output = j$(Cluster).$(Process)_stdout
Log = j$(Cluster).$(Process)_log
JobBatchName = {{ batch_name }}
should_transfer_files = YES
when_to_transfer_output = ON_EXIT
transfer_input_files = {{ input_files | join(', ') }}
transfer_output_files = j$(Cluster).$(Process)_report.json, j$(Cluster).$(Process)_report.txt, j$(Cluster).$(Process)_histogram.svg
arguments = $(Cluster) $(Process)
server:
num-threads: 1
interface: 127.0.0.1@{{ port }}
{% if listen_ipv6 %}
interface: ::1@{{ port }}
{%- endif %}
{% if tls_port %}
interface: 127.0.0.1@{{ tls_port }}
{% if listen_ipv6 %}
interface: ::1@{{ tls_port }}
{%- endif %}
ssl-port: {{ tls_port }}
ssl-service-key: "/usr/local/etc/unbound/key.pem"
ssl-service-pem: "/usr/local/etc/unbound/cert.pem"
{%- endif %}
so-reuseport: yes
module-config: "validator iterator"
trust-anchor-file: "/usr/local/etc/unbound/root.keys"
msg-cache-size: 400m
msg-cache-slabs: 1
rrset-cache-size: 400m
rrset-cache-slabs: 1
infra-cache-numhosts: 100000
val-log-level: 2
key-cache-size: 100m
neg-cache-size: 100m
respdiff:
srcdir: /var/opt/respdiff
jobs: 128
target: kresd
database:
url: https://gitlab.labs.nic.cz/knot/respdiff/uploads/69e90a46226984d08e096424dd81397d/shortlist.mdb
dest: data.mdb
remove_after: true
resolvers:
unbound:
type: unbound
port: &unbound_port 53701
respdiff:
ip: 127.0.0.1
port: *unbound_port
transport: udp
graph_color: '#218669'
bind:
type: bind
port: &bind_port 53711
forward: &fwd_unbound
ip: 127.0.0.1
port: *unbound_port
respdiff:
ip: 127.0.0.1
port: *bind_port
transport: udp
graph_color: '#e2a000'
kresd_fwd_target:
type: knot-resolver
port: 53722
tls_port: &kresd_fwd_target_port 53729
forward: *fwd_unbound
listen_ipv6: true
kresd:
type: knot-resolver
port: &kresd_port 53721
listen_ipv6: true
forward:
ip: '::1'
port: *kresd_fwd_target_port
tls: true
respdiff:
ip: '::1'
port: *kresd_port
transport: udp
restart_script: ./restart-all.sh
graph_color: '#00a2e2'