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

Proposed by William Grant
Status: Merged
Merged at revision: 18219
Proposed branch: lp:~wgrant/launchpad/config-overlay-dir
Merge into: lp:launchpad
Diff against target: 152 lines (+75/-3)
3 files modified
lib/lp/services/config/__init__.py (+15/-1)
lib/lp/services/config/schema-lazr.conf (+6/-0)
lib/lp/services/config/tests/test_config.py (+54/-2)
To merge this branch: bzr merge lp:~wgrant/launchpad/config-overlay-dir
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+307493@code.launchpad.net

Commit message

Support a config overlay directory.

Description of the change

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.

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/services/config/__init__.py'
2--- lib/lp/services/config/__init__.py 2012-08-09 04:41:57 +0000
3+++ lib/lp/services/config/__init__.py 2016-10-04 11:20:35 +0000
4@@ -1,4 +1,4 @@
5-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
6+# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
7 # GNU Affero General Public License version 3 (see the file LICENSE).
8
9 '''
10@@ -11,6 +11,7 @@
11 __metaclass__ = type
12
13
14+import glob
15 import logging
16 import os
17 import sys
18@@ -213,6 +214,7 @@
19 config_file = os.path.join(config_dir, 'launchpad-lazr.conf')
20 schema = ImplicitTypeSchema(schema_file)
21 self._config = schema.load(config_file)
22+ self._loadConfigOverlays(config_file)
23 try:
24 self._config.validate()
25 except ConfigErrors as error:
26@@ -220,6 +222,18 @@
27 raise ConfigErrors(message)
28 self._setZConfig()
29
30+ def _loadConfigOverlays(self, config_file):
31+ """Apply config overlays from the launchpad.config_overlay_dir."""
32+ rel_dir = self._config['launchpad']['config_overlay_dir']
33+ if not rel_dir:
34+ return
35+ dir = os.path.join(
36+ os.path.dirname(os.path.abspath(config_file)), rel_dir)
37+ for path in sorted(glob.glob(os.path.join(dir, '*-lazr.conf'))):
38+ with open(path) as f:
39+ text = f.read()
40+ self._config.push(path, text)
41+
42 @property
43 def zope_config_file(self):
44 """Return the path to the ZConfig file for this instance."""
45
46=== modified file 'lib/lp/services/config/schema-lazr.conf'
47--- lib/lp/services/config/schema-lazr.conf 2016-09-02 18:21:07 +0000
48+++ lib/lp/services/config/schema-lazr.conf 2016-10-04 11:20:35 +0000
49@@ -840,6 +840,12 @@
50
51
52 [launchpad]
53+# A directory of files from which *-lazr.conf will be loaded and
54+# overlaid in order on top of the main config.
55+# Note that this is relative to the top-level config file, not
56+# necessarily the one where the setting is actually configured!
57+config_overlay_dir:
58+
59 # The database user which will be used by this process.
60 # datatype: string
61 dbuser: launchpad_main
62
63=== modified file 'lib/lp/services/config/tests/test_config.py'
64--- lib/lp/services/config/tests/test_config.py 2013-01-07 03:47:45 +0000
65+++ lib/lp/services/config/tests/test_config.py 2016-10-04 11:20:35 +0000
66@@ -1,4 +1,4 @@
67-# Copyright 2009 Canonical Ltd. This software is licensed under the
68+# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
69 # GNU Affero General Public License version 3 (see the file LICENSE).
70
71 # We know we are not using root and handlers.
72@@ -15,12 +15,15 @@
73 import os
74 import unittest
75
76+from fixtures import TempDir
77 from lazr.config import ConfigSchema
78 from lazr.config.interfaces import ConfigErrors
79 import pkg_resources
80+import testtools
81 import ZConfig
82
83 import lp.services.config
84+from lp.services.config.fixture import ConfigUseFixture
85
86 # Configs that shouldn't be tested.
87 EXCLUDED_CONFIGS = ['lpnet-template']
88@@ -68,7 +71,7 @@
89 return LAZRConfigTestCase
90
91
92-class TestLaunchpadConfig(unittest.TestCase):
93+class TestLaunchpadConfig(testtools.TestCase):
94
95 def test_dir(self):
96 # dir(config) returns methods, variables and section names.
97@@ -88,6 +91,55 @@
98 sections = set(config._config)
99 self.assertEqual(sections, set(config))
100
101+ def test_override_directory(self):
102+ # The launchpad.config_overlay_dir setting can be used to load
103+ # extra config files over the top. This is useful for overlaying
104+ # non-version-controlled secrets.
105+ config_dir = self.useFixture(TempDir(rootdir='configs')).path
106+ config_name = os.path.basename(config_dir)
107+ overlay_dir = self.useFixture(TempDir(rootdir='configs')).path
108+ with open(os.path.join(config_dir, 'launchpad-lazr.conf'), 'w') as f:
109+ f.write("""
110+ [meta]
111+ extends: ../testrunner/launchpad-lazr.conf
112+
113+ [launchpad]
114+ config_overlay_dir: ../%s
115+ """ % os.path.basename(overlay_dir))
116+ os.symlink(
117+ '../testrunner/launchpad.conf',
118+ os.path.join(config_dir, 'launchpad.conf'))
119+
120+ config = lp.services.config.config
121+
122+ with ConfigUseFixture(config_name):
123+ self.assertEqual('launchpad_main', config.launchpad.dbuser)
124+ self.assertEqual('', config.launchpad.site_message)
125+
126+ with open(os.path.join(overlay_dir, '00-test-lazr.conf'), 'w') as f:
127+ f.write("""
128+ [launchpad]
129+ dbuser: overlay-user
130+ site_message: An overlay!
131+ """)
132+ with ConfigUseFixture(config_name):
133+ self.assertEqual('overlay-user', config.launchpad.dbuser)
134+ self.assertEqual('An overlay!', config.launchpad.site_message)
135+
136+ with open(os.path.join(overlay_dir, '01-test-lazr.conf'), 'w') as f:
137+ f.write("""
138+ [launchpad]
139+ site_message: Another overlay!
140+ """)
141+ with ConfigUseFixture(config_name):
142+ self.assertEqual('overlay-user', config.launchpad.dbuser)
143+ self.assertEqual('Another overlay!', config.launchpad.site_message)
144+
145+ os.unlink(os.path.join(overlay_dir, '00-test-lazr.conf'))
146+ with ConfigUseFixture(config_name):
147+ self.assertEqual('launchpad_main', config.launchpad.dbuser)
148+ self.assertEqual('Another overlay!', config.launchpad.site_message)
149+
150
151 def test_suite():
152 """Return a suite of canonical.conf and all conf files."""