You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by ja...@apache.org on 2016/06/14 18:43:11 UTC

[trafficserver] branch master updated: TS-4407 tsqa tests for hostdb (#710)

This is an automated email from the ASF dual-hosted git repository.

jacksontj pushed a commit to branch master
in repository https://git-dual.apache.org/repos/asf/trafficserver.git

The following commit(s) were added to refs/heads/master by this push:
       new  80213e3   TS-4407 tsqa tests for hostdb (#710)
80213e3 is described below

commit 80213e3669b2bcfa839a2ebc8fa686a8b707ede0
Author: Thomas Jackson <ja...@gmail.com>
AuthorDate: Tue Jun 14 11:43:05 2016 -0700

    TS-4407 tsqa tests for hostdb (#710)
    
    This includes a resolver that you can control from within the test
    
    This closes #710
---
 ci/tsqa/requirements.txt     |   1 +
 ci/tsqa/tests/test_hostdb.py | 185 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 186 insertions(+)

diff --git a/ci/tsqa/requirements.txt b/ci/tsqa/requirements.txt
index 01a8816..0aa7ecf 100644
--- a/ci/tsqa/requirements.txt
+++ b/ci/tsqa/requirements.txt
@@ -6,3 +6,4 @@ pyyaml
 pyOpenSSL
 # TODO: can't do python_version in requirements.txt files
 #hyper; python_version >= '2.7'
+dnslib
diff --git a/ci/tsqa/tests/test_hostdb.py b/ci/tsqa/tests/test_hostdb.py
index 3a9b800..614a238 100644
--- a/ci/tsqa/tests/test_hostdb.py
+++ b/ci/tsqa/tests/test_hostdb.py
@@ -22,14 +22,61 @@ import os
 import requests
 import time
 import logging
+import socket
 import SocketServer
 
+import contextlib
+import dnslib
+import dnslib.server
+
 import tsqa.test_cases
 import helpers
 
 log = logging.getLogger(__name__)
 
 
+@contextlib.contextmanager
+def kill_dns(dns_server):
+    ''' Temporarily kill the dns server
+    '''
+    dns_server.stop()
+    yield
+    dns_server.start_thread()
+
+
+class StubDNSResolver(object):
+    '''Resolver to serve defined responses from `response_dict` or return SOA
+    '''
+    def __init__(self, responses):
+        self.responses = responses
+        self.resp_headers = {}
+
+    def resolve(self, request, handler):
+        reply = request.reply()
+        for q in request.questions:
+            qname = str(q.get_qname())
+            if qname in self.responses:
+                for resp in self.responses[qname]:
+                    reply.add_answer(resp)
+            else:
+                reply.add_answer(dnslib.server.RR(
+                    q.get_qname(),
+                    rtype=dnslib.QTYPE.SOA,
+                    ttl=1,
+                    rdata=dnslib.dns.SOA(
+                        'nameserver.local',
+                        q.get_qname(),
+                    ),
+                ))
+        for k, v in self.resp_headers.iteritems():
+            if k == 'rcode':
+                reply.header.set_rcode(v)
+                print 'setting rcode'
+            else:
+                log.warning('Unsupported header sent to StubDNSResolver %s' % k)
+        return reply
+
+
 class EchoServerIpHandler(SocketServer.BaseRequestHandler):
     """
     A subclass of RequestHandler which will return a connection uuid
@@ -214,3 +261,141 @@ class TestHostDBHostsFile(helpers.EnvironmentCase, tsqa.test_cases.HTTPBinCase):
         )
         self.assertEqual(ret.status_code, 200)
         self.assertEqual('127.0.0.3', ret.headers['X-Server-Ip'])
+
+
+class TestHostDB(helpers.EnvironmentCase, tsqa.test_cases.HTTPBinCase):
+    @classmethod
+    def setUpEnv(cls, env):
+        cls.dns_sock = socket.socket (socket.AF_INET, socket.SOCK_DGRAM)
+        cls.dns_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        cls.dns_sock.bind(('', 0))  # bind to all interfaces on an ephemeral port
+        dns_port = cls.dns_sock.getsockname()[1]
+
+        # set up dns resolver
+        cls.responses = {
+            'www.foo.com.': dnslib.server.RR.fromZone("foo.com. 1 A 127.0.0.1"),
+            'www.stale_for.com.': dnslib.server.RR.fromZone("foo.com. 1 A 127.0.0.1"),
+        }
+
+        cls.dns_server = dnslib.server.DNSServer(
+            StubDNSResolver(cls.responses),
+            port=dns_port,
+            address="localhost",
+        )
+        cls.dns_server.start_thread()
+
+        cls.hosts_file_path = os.path.join(env.layout.prefix, 'resolv')
+        with open(cls.hosts_file_path, 'w') as fh:
+            fh.write('nameserver 127.0.0.1:{0}\n'.format(dns_port))
+
+        cls.configs['records.config']['CONFIG'].update({
+            'proxy.config.http.response_server_enabled': 2,  # only add server headers when there weren't any
+            'proxy.config.hostdb.lookup_timeout': 1,
+            'proxy.config.url_remap.remap_required': 0,
+            'proxy.config.http.connect_attempts_max_retries': 1,
+            'proxy.config.diags.debug.enabled': 1,
+            'proxy.config.diags.debug.tags': 'hostdb',
+            'proxy.config.dns.resolv_conf': os.path.join(env.layout.prefix, 'resolv'),
+            'proxy.config.hostdb.serve_stale_for': 2,
+            'proxy.config.hostdb.ttl_mode': 0,
+            'proxy.config.http_ui_enabled': 3,
+        })
+
+        cls.configs['remap.config'].add_line('map /_hostdb/ http://{hostdb}')
+
+    def _hostdb_entries(self):
+        # mapping of name -> entries
+        ret = {}
+        showall_ret = requests.get('http://127.0.0.1:{0}/_hostdb/showall?format=json'.format(
+            self.configs['records.config']['CONFIG']['proxy.config.http.server_ports']
+        ), timeout=1).json()
+
+        for item in showall_ret:
+            ret[item['hostname']] = item
+
+        return ret
+
+    def test_dns(self):
+        '''Test that DNS lookups end up in hostdb as we expect
+        '''
+        # TODO: remove
+        self.test_basic()
+        print self._hostdb_entries()
+
+        # test something with a LARGE number of entries
+        zone_parts = []
+        # TODO: fix this, right now there is `#define DNS_MAX_ADDRS 35` which
+        # controls how many work-- we should make this configurable
+        # 30 works, once you pass 35 some records are missing, and at some point
+        # you start getting garbage (50 for example) and at some point (100) it
+        # seems to crash
+        NUM_RECORDS = 2
+        for x in xrange(0, NUM_RECORDS):
+            zone_parts.append("www.huge.com. 1 A 127.0.0.{0}".format(x + 1))
+        self.responses['www.huge.com.'] = dnslib.server.RR.fromZone('\n'.join(zone_parts))
+
+        ret = requests.get(
+            'http://www.huge.com:{0}/get'.format(self.http_endpoint.address[1]),
+            proxies=self.proxies,
+        )
+        #self.assertEqual(ret.status_code, 200)
+
+        for item in self._hostdb_entries()['www.huge.com']['rr_records']:
+            print item['ip']
+
+        self.assertEqual(len(self._hostdb_entries()['www.huge.com']['rr_records']), NUM_RECORDS)
+
+
+    def test_basic(self):
+        '''
+        Test basic fnctionality of resolver
+        '''
+
+        # test one that works
+        ret = requests.get(
+            'http://www.foo.com:{0}/get'.format(self.http_endpoint.address[1]),
+            proxies=self.proxies,
+        )
+        self.assertEqual(ret.status_code, 200)
+
+        # check one that doesn't exist
+        ret = requests.get(
+            'http://www.bar.com:{0}/get'.format(self.http_endpoint.address[1]),
+            proxies=self.proxies,
+        )
+        self.assertEqual(ret.status_code, 502)
+
+    def test_serve_stail_for(self):
+        start = time.time()
+        ret = requests.get(
+            'http://www.stale_for.com:{0}/get'.format(self.http_endpoint.address[1]),
+            proxies=self.proxies,
+        )
+        self.assertEqual(ret.status_code, 200)
+        # mark the DNSServer down
+        with kill_dns(self.dns_server):
+            timeout_at = time.time() + 10
+            end_working = None
+            end = None
+            count = 0
+
+            while time.time() < timeout_at:
+                ret = requests.get(
+                    'http://www.stale_for.com:{0}/get'.format(self.http_endpoint.address[1]),
+                    proxies=self.proxies,
+                )
+                count += 1
+                if ret.status_code != 200:
+                    end = time.time()
+                    break
+                else:
+                    end_working = time.time()
+                time.sleep(0.5)
+            # ensure that it was for at least 2 seconds
+            print end_working - start
+            self.assertTrue(end_working - start >= 2)
+            # TODO: Fix this!
+            # for whatever reason the failed DNS response is taking ~3.5s to timeout
+            # even though the hostdb.lookup_timeout is set to 1 (meaning it should be ~1s)
+            #print end - end_working
+            #self.assertTrue(end - start >= 2)

-- 
To stop receiving notification emails like this one, please contact
['"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>'].