Sfoglia il codice sorgente

feat(pdns): command lord to NOTIFY master immediately on update

This decreases DNS update time at the nsmaster to approx. one 1 second.

Adopted unit tests, as most tests now need to access the last-but-one
request to make their assertions.

Added a unit test to see if our code PUTs on the /notify endpoint of
the pdns API.

Calling notify_zone immediately after creating a zone results in a
500 error at the pdns API. For discussion, see also #40.
Nils Wisiol 8 anni fa
parent
commit
67a3737a97

+ 16 - 0
api/desecapi/pdns.py

@@ -62,6 +62,13 @@ def _pdns_get(url):
     return r
 
 
+def _pdns_put(url):
+    r = requests.put(settings.NSLORD_PDNS_API + url, headers=headers_nslord)
+    if r.status_code < 200 or r.status_code >= 500:
+        raise Exception(r.text)
+    return r
+
+
 def _delete_or_replace_rrset(name, type, value, ttl=60):
     """
     Return pdns API json to either replace or delete a record set, depending on whether value is empty or not.
@@ -127,6 +134,13 @@ def zone_exists(name):
         raise Exception(reply.text)
 
 
+def notify_zone(name):
+    """
+    Commands pdns to notify the zone to the pdns slaves.
+    """
+    return _pdns_put('/zones/%s/notify' % normalize_hostname(name))
+
+
 def set_dyn_records(name, a, aaaa, acme_challenge=''):
     """
     Commands pdns to set the A and AAAA record for the zone with the given name to the given record values.
@@ -143,6 +157,8 @@ def set_dyn_records(name, a, aaaa, acme_challenge=''):
         ]
     })
 
+    notify_zone(name)
+
 
 def set_rrset(zone, name, type, value):
     """

+ 28 - 10
api/desecapi/tests/testdomains.py

@@ -197,16 +197,17 @@ class AuthenticatedDomainTests(APITestCase):
         httpretty.enable()
         httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones')
         httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + name + '.')
+        httpretty.register_uri(httpretty.PUT, settings.NSLORD_PDNS_API + '/zones/' + name + './notify')
 
         url = reverse('domain-list')
         data = {'name': name, 'arecord': '1.3.3.7', 'aaaarecord': 'dead::beef', 'acme_challenge': 'letsencrypt_ftw'}
         response = self.client.post(url, data)
 
-        self.assertEqual(httpretty.last_request().method, 'PATCH')
-        self.assertTrue(data['name'] in httpretty.last_request().parsed_body)
-        self.assertTrue('1.3.3.7' in httpretty.last_request().parsed_body)
-        self.assertTrue('dead::beef' in httpretty.last_request().parsed_body)
-        self.assertTrue('letsencrypt_ftw' in httpretty.last_request().parsed_body)
+        self.assertEqual(httpretty.httpretty.latest_requests[-2].method, 'PATCH')
+        self.assertTrue(data['name'] in httpretty.httpretty.latest_requests[-2].parsed_body)
+        self.assertTrue('1.3.3.7' in httpretty.httpretty.latest_requests[-2].parsed_body)
+        self.assertTrue('dead::beef' in httpretty.httpretty.latest_requests[-2].parsed_body)
+        self.assertTrue('letsencrypt_ftw' in httpretty.httpretty.latest_requests[-2].parsed_body)
 
     def testPostDomainCausesPdnsAPIPatch(self):
         name = utils.generateDomainname()
@@ -214,26 +215,43 @@ class AuthenticatedDomainTests(APITestCase):
         httpretty.enable()
         httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones')
         httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + name + '.')
+        httpretty.register_uri(httpretty.PUT, settings.NSLORD_PDNS_API + '/zones/' + name + './notify')
 
         url = reverse('domain-list')
         data = {'name': name, 'acme_challenge': 'letsencrypt_ftw'}
         self.client.post(url, data)
 
-        self.assertEqual(httpretty.last_request().method, 'PATCH')
-        self.assertTrue(data['name'] in httpretty.last_request().parsed_body)
-        self.assertTrue('letsencrypt_ftw' in httpretty.last_request().parsed_body)
+        self.assertEqual(httpretty.httpretty.latest_requests[-2].method, 'PATCH')
+        self.assertTrue(data['name'] in httpretty.httpretty.latest_requests[-2].parsed_body)
+        self.assertTrue('letsencrypt_ftw' in httpretty.httpretty.latest_requests[-2].parsed_body)
 
-    def testUpdateingCausesPdnsAPICall(self):
+    def testUpdateingCausesPdnsAPIPatchCall(self):
         url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
         response = self.client.get(url)
 
         httpretty.enable()
         httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + response.data['name'] + '.')
+        httpretty.register_uri(httpretty.PUT, settings.NSLORD_PDNS_API + '/zones/' + response.data['name'] + './notify')
 
         response.data['arecord'] = '10.13.3.7'
         response = self.client.put(url, response.data)
 
-        self.assertTrue('10.13.3.7' in httpretty.last_request().parsed_body)
+        self.assertTrue('10.13.3.7' in httpretty.httpretty.latest_requests[-2].parsed_body)
+
+    def testUpdateingCausesPdnsAPINotifyCall(self):
+        url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
+        response = self.client.get(url)
+
+        httpretty.enable()
+        httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + response.data['name'] + '.')
+        httpretty.register_uri(httpretty.PUT, settings.NSLORD_PDNS_API + '/zones/' + response.data['name'] + './notify')
+
+        response.data['arecord'] = '10.13.3.10'
+        response = self.client.put(url, response.data)
+
+        self.assertEqual(httpretty.httpretty.latest_requests[-2].method, 'PATCH')
+        self.assertTrue('10.13.3.10' in httpretty.httpretty.latest_requests[-2].parsed_body)
+        self.assertEqual(httpretty.last_request().method, 'PUT')
 
     def testDomainDetailURL(self):
         url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))

+ 12 - 8
api/desecapi/tests/testdyndns12update.py

@@ -33,6 +33,7 @@ class DynDNS12UpdateTest(APITestCase):
         httpretty.HTTPretty.allow_net_connect = False
         httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones')
         httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + self.domain + '.')
+        httpretty.register_uri(httpretty.PUT, settings.NSLORD_PDNS_API + '/zones/' + self.domain + './notify')
 
     def tearDown(self):
         httpretty.disable()
@@ -183,13 +184,14 @@ class DynDNS12UpdateTest(APITestCase):
 
         httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + self.domain + '.')
         httpretty.register_uri(httpretty.GET, settings.NSLORD_PDNS_API + '/zones/' + self.domain + '.', status=200)
+        httpretty.register_uri(httpretty.PUT, settings.NSLORD_PDNS_API + '/zones/' + self.domain + './notify', status=200)
 
         self.owner.unlock()
 
-        self.assertEqual(httpretty.last_request().method, 'PATCH')
-        self.assertTrue((settings.NSLORD_PDNS_API + '/zones/' + self.domain + '.').endswith(httpretty.last_request().path))
-        self.assertTrue(self.domain in httpretty.last_request().parsed_body)
-        self.assertTrue('10.1.1.1' in httpretty.last_request().parsed_body)
+        self.assertEqual(httpretty.httpretty.latest_requests[-2].method, 'PATCH')
+        self.assertTrue((settings.NSLORD_PDNS_API + '/zones/' + self.domain + '.').endswith(httpretty.httpretty.latest_requests[-2].path))
+        self.assertTrue(self.domain in httpretty.httpretty.latest_requests[-2].parsed_body)
+        self.assertTrue('10.1.1.1' in httpretty.httpretty.latest_requests[-2].parsed_body)
 
     def testSuspendedUpdatesDomainCreation(self):
         self.owner.captcha_required = True
@@ -213,14 +215,16 @@ class DynDNS12UpdateTest(APITestCase):
         httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones')
         httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + newdomain + '.')
         httpretty.register_uri(httpretty.GET, settings.NSLORD_PDNS_API + '/zones/' + newdomain + '.', status=200)
+        httpretty.register_uri(httpretty.PUT, settings.NSLORD_PDNS_API + '/zones/' + newdomain + './notify', status=200)
         httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + self.domain + '.')
         httpretty.register_uri(httpretty.GET, settings.NSLORD_PDNS_API + '/zones/' + self.domain + '.', status=200)
+        httpretty.register_uri(httpretty.PUT, settings.NSLORD_PDNS_API + '/zones/' + self.domain + './notify', status=200)
 
         self.owner.unlock()
 
-        self.assertEqual(httpretty.last_request().method, 'PATCH')
+        self.assertEqual(httpretty.httpretty.latest_requests[-2].method, 'PATCH')
         self.assertTrue(
-                (settings.NSLORD_PDNS_API + '/zones/' + self.domain + '.').endswith(httpretty.last_request().path) \
-                or (settings.NSLORD_PDNS_API + '/zones/' + newdomain + '.').endswith(httpretty.last_request().path)
+                (settings.NSLORD_PDNS_API + '/zones/' + self.domain + '.').endswith(httpretty.httpretty.latest_requests[-2].path) \
+                or (settings.NSLORD_PDNS_API + '/zones/' + newdomain + '.').endswith(httpretty.httpretty.latest_requests[-2].path)
             )
-        self.assertTrue('10.2.2.2' in httpretty.last_request().parsed_body)
+        self.assertTrue('10.2.2.2' in httpretty.httpretty.latest_requests[-2].parsed_body)

+ 1 - 0
api/desecapi/tests/testdynupdateauthentication.py

@@ -32,6 +32,7 @@ class DynUpdateAuthenticationTests(APITestCase):
             httpretty.enable()
             httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones')
             httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + self.domain + '.')
+            httpretty.register_uri(httpretty.PUT, settings.NSLORD_PDNS_API + '/zones/' + self.domain + './notify')
 
     def testSuccessfulAuthentication(self):
         response = self.client.get(self.url)