← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wgrant/launchpad/config-overlay-dir into lp:launchpad

 

William Grant has proposed merging lp:~wgrant/launchpad/config-overlay-dir into lp:launchpad.

Commit message:
Support a config overlay directory.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~wgrant/launchpad/config-overlay-dir/+merge/307493

Support a config overlay directory.

If launchpad-lazr.conf configures launchpad.config_overlay_dir,
*-lazr.conf within that directory (relative to the config file) will be
pushed onto the top of the config stack in lexicographic order.

This is handy for non-version-controlled secrets and similar.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/config-overlay-dir into lp:launchpad.
=== modified file 'lib/lp/services/config/__init__.py'
--- lib/lp/services/config/__init__.py	2012-08-09 04:41:57 +0000
+++ lib/lp/services/config/__init__.py	2016-10-04 00:02:09 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2016 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 '''
@@ -11,6 +11,7 @@
 __metaclass__ = type
 
 
+import glob
 import logging
 import os
 import sys
@@ -213,6 +214,7 @@
             config_file = os.path.join(config_dir, 'launchpad-lazr.conf')
         schema = ImplicitTypeSchema(schema_file)
         self._config = schema.load(config_file)
+        self._loadConfigOverlays(config_file)
         try:
             self._config.validate()
         except ConfigErrors as error:
@@ -220,6 +222,16 @@
             raise ConfigErrors(message)
         self._setZConfig()
 
+    def _loadConfigOverlays(self, config_file):
+        """Apply config overlays from the launchpad.config_overlay_dir."""
+        rel_dir = self._config['launchpad']['config_overlay_dir']
+        dir = os.path.join(
+            os.path.dirname(os.path.abspath(config_file)), rel_dir)
+        for path in sorted(glob.glob(os.path.join(dir, '*-lazr.conf'))):
+            with open(path) as f:
+                text = f.read()
+            self._config.push(path, text)
+
     @property
     def zope_config_file(self):
         """Return the path to the ZConfig file for this instance."""

=== modified file 'lib/lp/services/config/schema-lazr.conf'
--- lib/lp/services/config/schema-lazr.conf	2016-09-02 18:21:07 +0000
+++ lib/lp/services/config/schema-lazr.conf	2016-10-04 00:02:09 +0000
@@ -840,6 +840,12 @@
 
 
 [launchpad]
+# A directory of files from which *-lazr.conf will be loaded and
+# overlayed in order on top of the main config.
+# Note that this is relative to the top-level config file, not
+# necessarily the one where the setting is actually configured!
+config_overlay_dir:
+
 # The database user which will be used by this process.
 # datatype: string
 dbuser: launchpad_main

=== modified file 'lib/lp/services/config/tests/test_config.py'
--- lib/lp/services/config/tests/test_config.py	2013-01-07 03:47:45 +0000
+++ lib/lp/services/config/tests/test_config.py	2016-10-04 00:02:09 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2016 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 # We know we are not using root and handlers.
@@ -15,12 +15,15 @@
 import os
 import unittest
 
+from fixtures import TempDir
 from lazr.config import ConfigSchema
 from lazr.config.interfaces import ConfigErrors
 import pkg_resources
+import testtools
 import ZConfig
 
 import lp.services.config
+from lp.services.config.fixture import ConfigUseFixture
 
 # Configs that shouldn't be tested.
 EXCLUDED_CONFIGS = ['lpnet-template']
@@ -68,7 +71,7 @@
     return LAZRConfigTestCase
 
 
-class TestLaunchpadConfig(unittest.TestCase):
+class TestLaunchpadConfig(testtools.TestCase):
 
     def test_dir(self):
         # dir(config) returns methods, variables and section names.
@@ -88,6 +91,55 @@
         sections = set(config._config)
         self.assertEqual(sections, set(config))
 
+    def test_override_directory(self):
+        # The launchpad.config_overlay_dir setting can be used to load
+        # extra config files over the top. This is useful for overlaying
+        # non-version-controlled secrets.
+        config_dir = self.useFixture(TempDir(rootdir='configs')).path
+        config_name = os.path.basename(config_dir)
+        overlay_dir = self.useFixture(TempDir(rootdir='configs')).path
+        with open(os.path.join(config_dir, 'launchpad-lazr.conf'), 'w') as f:
+            f.write("""
+                [meta]
+                extends: ../testrunner/launchpad-lazr.conf
+
+                [launchpad]
+                config_overlay_dir: ../%s
+                """ % os.path.basename(overlay_dir))
+        os.symlink(
+            '../testrunner/launchpad.conf',
+            os.path.join(config_dir, 'launchpad.conf'))
+
+        config = lp.services.config.config
+
+        with ConfigUseFixture(config_name):
+            self.assertEqual('launchpad_main', config.launchpad.dbuser)
+            self.assertEqual('', config.launchpad.site_message)
+
+        with open(os.path.join(overlay_dir, '00-test-lazr.conf'), 'w') as f:
+            f.write("""
+                [launchpad]
+                dbuser: overlay-user
+                site_message: An overlay!
+                """)
+        with ConfigUseFixture(config_name):
+            self.assertEqual('overlay-user', config.launchpad.dbuser)
+            self.assertEqual('An overlay!', config.launchpad.site_message)
+
+        with open(os.path.join(overlay_dir, '01-test-lazr.conf'), 'w') as f:
+            f.write("""
+                [launchpad]
+                site_message: Another overlay!
+                """)
+        with ConfigUseFixture(config_name):
+            self.assertEqual('overlay-user', config.launchpad.dbuser)
+            self.assertEqual('Another overlay!', config.launchpad.site_message)
+
+        os.unlink(os.path.join(overlay_dir, '00-test-lazr.conf'))
+        with ConfigUseFixture(config_name):
+            self.assertEqual('launchpad_main', config.launchpad.dbuser)
+            self.assertEqual('Another overlay!', config.launchpad.site_message)
+
 
 def test_suite():
     """Return a suite of canonical.conf and all conf files."""


Follow ups