Browse Source

[1789] address review comments

All in xfrin memory zones handlers;
- normalize zone name and class
- provide strong exception guarantee in _set_memory_zones
- fix comment in _set_db_file
- make _auth_config_update 'protected'

And in the tests:
- extracted memory zones update tests to own class
- added tests for bad data
- added tests for normalization
Jelte Jansen 13 years ago
parent
commit
94077743ff
2 changed files with 133 additions and 52 deletions
  1. 91 31
      src/bin/xfrin/tests/xfrin_test.py
  2. 42 21
      src/bin/xfrin/xfrin.py.in

+ 91 - 31
src/bin/xfrin/tests/xfrin_test.py

@@ -2577,61 +2577,64 @@ class TestXfrin(unittest.TestCase):
         self.common_ixfr_setup('refresh', False)
         self.common_ixfr_setup('refresh', False)
         self.assertEqual(RRType.AXFR(), self.xfr.xfrin_started_request_type)
         self.assertEqual(RRType.AXFR(), self.xfr.xfrin_started_request_type)
 
 
-    def test_memory_zones(self):
+class TextXfrinMemoryZones(unittest.TestCase):
+    def setUp(self):
+        self.xfr = MockXfrin()
         # Configuration snippet containing 2 memory datasources,
         # Configuration snippet containing 2 memory datasources,
         # one for IN and one for CH. Both contain a zone 'example.com'
         # one for IN and one for CH. Both contain a zone 'example.com'
         # the IN ds also contains a zone example2.com, and a zone example3.com,
         # the IN ds also contains a zone example2.com, and a zone example3.com,
         # which is of file type 'text' (and hence, should be ignored)
         # which is of file type 'text' (and hence, should be ignored)
-        config = { 'datasources': [
+        self.config = { 'datasources': [
-                     { 'type': 'memory',
+                          { 'type': 'memory',
-                       'class': 'IN',
+                            'class': 'IN',
-                       'zones': [
+                            'zones': [
-                         { 'origin': 'example.com',
+                              { 'origin': 'example.com',
-                           'filetype': 'sqlite3' },
+                                'filetype': 'sqlite3' },
-                         { 'origin': 'example2.com',
+                              { 'origin': 'EXAMPLE2.com.',
-                           'filetype': 'sqlite3' },
+                                'filetype': 'sqlite3' },
-                         { 'origin': 'example3.com',
+                              { 'origin': 'example3.com',
-                           'filetype': 'text' }
+                                'filetype': 'text' }
-                       ]
+                            ]
-                     },
+                          },
-                     { 'type': 'memory',
+                          { 'type': 'memory',
-                       'class': 'CH',
+                            'class': 'CH',
-                       'zones': [
+                            'zones': [
-                         { 'origin': 'example.com',
+                              { 'origin': 'example.com',
-                           'filetype': 'sqlite3' }
+                                'filetype': 'sqlite3' }
-                       ]
+                            ]
-                     }
+                          }
-                 ] }
+                      ] }
-
+
+    def test_updates(self):
         self.assertFalse(self.xfr._is_memory_zone("example.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example2.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example2.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example.com", "CH"))
         self.assertFalse(self.xfr._is_memory_zone("example.com", "CH"))
 
 
         # add them all
         # add them all
-        self.xfr._set_memory_zones(config, None)
+        self.xfr._set_memory_zones(self.config, None)
         self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
         self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
         self.assertTrue(self.xfr._is_memory_zone("example2.com", "IN"))
         self.assertTrue(self.xfr._is_memory_zone("example2.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
         self.assertTrue(self.xfr._is_memory_zone("example.com", "CH"))
         self.assertTrue(self.xfr._is_memory_zone("example.com", "CH"))
 
 
-        # Remove the CH data source from the config snippet, and update
+        # Remove the CH data source from the self.config snippet, and update
-        del config['datasources'][1]
+        del self.config['datasources'][1]
-        self.xfr._set_memory_zones(config, None)
+        self.xfr._set_memory_zones(self.config, None)
         self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
         self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
         self.assertTrue(self.xfr._is_memory_zone("example2.com", "IN"))
         self.assertTrue(self.xfr._is_memory_zone("example2.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example.com", "CH"))
         self.assertFalse(self.xfr._is_memory_zone("example.com", "CH"))
 
 
         # Remove example2.com from the datasource, and update
         # Remove example2.com from the datasource, and update
-        del config['datasources'][0]['zones'][1]
+        del self.config['datasources'][0]['zones'][1]
-        self.xfr._set_memory_zones(config, None)
+        self.xfr._set_memory_zones(self.config, None)
         self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
         self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example2.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example2.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example.com", "CH"))
         self.assertFalse(self.xfr._is_memory_zone("example.com", "CH"))
 
 
-        # If 'datasources' is not in the config update list (i.e. its config
+        # If 'datasources' is not in the self.config update list (i.e. its self.config
         # has not changed), no difference should be found
         # has not changed), no difference should be found
         self.xfr._set_memory_zones({}, None)
         self.xfr._set_memory_zones({}, None)
         self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
         self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
@@ -2640,13 +2643,70 @@ class TestXfrin(unittest.TestCase):
         self.assertFalse(self.xfr._is_memory_zone("example.com", "CH"))
         self.assertFalse(self.xfr._is_memory_zone("example.com", "CH"))
 
 
         # If datasources list becomes empty, everything should be removed
         # If datasources list becomes empty, everything should be removed
-        config['datasources'][0]['zones'] = []
+        self.config['datasources'][0]['zones'] = []
-        self.xfr._set_memory_zones(config, None)
+        self.xfr._set_memory_zones(self.config, None)
         self.assertFalse(self.xfr._is_memory_zone("example.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example2.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example2.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
         self.assertFalse(self.xfr._is_memory_zone("example.com", "CH"))
         self.assertFalse(self.xfr._is_memory_zone("example.com", "CH"))
 
 
+    def test_normalization(self):
+        self.xfr._set_memory_zones(self.config, None)
+        # make sure it is case insensitive, root-dot-insensitive,
+        # and supports CLASSXXX notation
+        self.assertTrue(self.xfr._is_memory_zone("EXAMPLE.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example2.com.", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "CLASS3"))
+        
+    def test_bad_name(self):
+        # First set it to some config
+        self.xfr._set_memory_zones(self.config, None)
+
+        # Error checking; bad owner name should result in no changes
+        self.config['datasources'][1]['zones'][0]['origin'] = ".."
+        self.xfr._set_memory_zones(self.config, None)
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example2.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "CH"))
+
+    def test_bad_class(self):
+        # First set it to some config
+        self.xfr._set_memory_zones(self.config, None)
+
+        # Error checking; bad owner name should result in no changes
+        self.config['datasources'][1]['class'] = "Foo"
+        self.xfr._set_memory_zones(self.config, None)
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example2.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "CH"))
+
+    def test_no_filetype(self):
+        # omitting the filetype should leave that zone out, but not
+        # the rest
+        del self.config['datasources'][1]['zones'][0]['filetype']
+        self.xfr._set_memory_zones(self.config, None)
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example2.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example.com", "CH"))
+
+    def test_class_filetype(self):
+        # omitting the class should have it default to what is in the
+        # specfile for Auth.
+        AuthConfigData = isc.config.config_data.ConfigData(
+            isc.config.module_spec_from_file(xfrin.AUTH_SPECFILE_LOCATION))
+        del self.config['datasources'][0]['class']
+        self.xfr._set_memory_zones(self.config, AuthConfigData)
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example2.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "CH"))
+
+
+
+
 def raise_interrupt():
 def raise_interrupt():
     raise KeyboardInterrupt()
     raise KeyboardInterrupt()
 
 

+ 42 - 21
src/bin/xfrin/xfrin.py.in

@@ -1270,7 +1270,7 @@ class Xfrin:
         config_data = self._module_cc.get_full_config()
         config_data = self._module_cc.get_full_config()
         self.config_handler(config_data)
         self.config_handler(config_data)
         self._module_cc.add_remote_config(AUTH_SPECFILE_LOCATION,
         self._module_cc.add_remote_config(AUTH_SPECFILE_LOCATION,
-                                          self.auth_config_handler)
+                                          self._auth_config_handler)
 
 
     def _cc_check_command(self):
     def _cc_check_command(self):
         '''This is a straightforward wrapper for cc.check_command,
         '''This is a straightforward wrapper for cc.check_command,
@@ -1317,7 +1317,7 @@ class Xfrin:
 
 
         return create_answer(0)
         return create_answer(0)
 
 
-    def auth_config_handler(self, new_config, config_data):
+    def _auth_config_handler(self, new_config, config_data):
         # Config handler for changes in Auth configuration
         # Config handler for changes in Auth configuration
         self._set_db_file()
         self._set_db_file()
         self._set_memory_zones(new_config, config_data)
         self._set_memory_zones(new_config, config_data)
@@ -1333,6 +1333,13 @@ class Xfrin:
            in the in-memory datasource of the Auth process with file type
            in the in-memory datasource of the Auth process with file type
            'sqlite3'.
            'sqlite3'.
         """
         """
+        # Normalize them first, if either conversion fails, return false
+        # (they won't be in the set anyway)
+        try:
+            zone_name_str = Name(zone_name_str).to_text().lower()
+            zone_class_str = RRClass(zone_class_str).to_text()
+        except Exception:
+            return False
         return (zone_name_str, zone_class_str) in self._memory_zones
         return (zone_name_str, zone_class_str) in self._memory_zones
 
 
     def _add_memory_zone(self, zone_name_str, zone_class_str):
     def _add_memory_zone(self, zone_name_str, zone_class_str):
@@ -1341,24 +1348,39 @@ class Xfrin:
         self._memory_zones.add((zone_name_str, zone_class_str))
         self._memory_zones.add((zone_name_str, zone_class_str))
 
 
     def _set_memory_zones(self, new_config, config_data):
     def _set_memory_zones(self, new_config, config_data):
-        """Part of the auth_config_handler function, keeps an internal set
+        """Part of the _auth_config_handler function, keeps an internal set
            of zones in the datasources config subset that have 'sqlite3' as
            of zones in the datasources config subset that have 'sqlite3' as
            their file type"""
            their file type"""
-        if "datasources" in new_config:
+        # walk through the data and collect the memory zones
-            self._clear_memory_zones()
+        # If this causes any exception, assume we were passed bad data
-            for datasource in new_config["datasources"]:
+        # and keep the original set
-                if "class" in datasource:
+        new_memory_zones = set()
-                    ds_class = datasource["class"]
+        try:
-                else:
+            if "datasources" in new_config:
-                    # Get the default
+                for datasource in new_config["datasources"]:
-                    ds_class =\
+                    if "class" in datasource:
-                        config_data.get_default_value("datasources/class")
+                        ds_class = RRClass(datasource["class"])
-                if datasource["type"] == "memory":
+                    else:
-                    zones = []
+                        # Get the default
-                    for zone in datasource["zones"]:
+                        ds_class = RRClass(config_data.get_default_value(
-                        if zone["filetype"] == "sqlite3":
+                                               "datasources/class"))
-                            zone_name = zone["origin"]
+                    if datasource["type"] == "memory":
-                            self._add_memory_zone(zone_name, ds_class)
+                        for zone in datasource["zones"]:
+                            if "filetype" in zone and \
+                               zone["filetype"] == "sqlite3":
+                                zone_name = Name(zone["origin"])
+                                zone_name_str = zone_name.to_text().lower()
+                                new_memory_zones.add((zone_name_str,
+                                                      ds_class.to_text()))
+                # Ok, we can use the data, update our list
+                self._clear_memory_zones()
+                for zone_str, class_str in new_memory_zones:
+                    self._add_memory_zone(zone_str, class_str)
+        except Exception as eee:
+            # Something is wrong with the data. If this data even reached us,
+            # we cannot do more than assume the real module has logged and
+            # reported an error. Keep the old set.
+            return
 
 
     def shutdown(self):
     def shutdown(self):
         ''' shutdown the xfrin process. the thread which is doing xfrin should be
         ''' shutdown the xfrin process. the thread which is doing xfrin should be
@@ -1503,9 +1525,8 @@ class Xfrin:
         db_file, is_default =\
         db_file, is_default =\
             self._module_cc.get_remote_config_value("Auth", "database_file")
             self._module_cc.get_remote_config_value("Auth", "database_file")
         if is_default and "B10_FROM_BUILD" in os.environ:
         if is_default and "B10_FROM_BUILD" in os.environ:
-            # this too should be unnecessary, but currently the
+            # override the local database setting if it is default and we
-            # 'from build' override isn't stored in the config
+            # are running from the source tree
-            # (and we don't have writable datasources yet)
             db_file = os.environ["B10_FROM_BUILD"] + os.sep +\
             db_file = os.environ["B10_FROM_BUILD"] + os.sep +\
                       "bind10_zones.sqlite3"
                       "bind10_zones.sqlite3"
         self._db_file = db_file
         self._db_file = db_file