Browse code

zuul: new variable to easily populate TEMPEST_PLUGINS

TEMPEST_PLUGINS contains the list of the tempest plugins installed
alongside tempest by lib/tempest.
If TEMPEST_PLUGINS is not explicitly set, the new tempest_plugins
variable is used to fill it by combining its items with
the base devstack path.

Change-Id: I9f1fa2755e16871ff9d6ba33fdeaf3023eedf8d4

Luigi Toscano authored on 2019/03/13 06:25:44
Showing 5 changed files
... ...
@@ -102,7 +102,6 @@ job.parent.
102 102
         tox_envlist: 'all'
103 103
         devstack_localrc:
104 104
           KURYR_K8S_API_PORT: 8080
105
-          TEMPEST_PLUGINS: '/opt/stack/kuryr-tempest-plugin'
106 105
         devstack_services:
107 106
           kubernetes-api: true
108 107
           kubernetes-controller-manager: true
... ...
@@ -114,6 +113,8 @@ job.parent.
114 114
           kuryr-kubernetes: https://git.openstack.org/openstack/kuryr
115 115
           devstack-plugin-container: https://git.openstack.org/openstack/devstack-plugin-container
116 116
           neutron-lbaas: https://git.openstack.org/openstack/neutron-lbaas
117
+        tempest_plugins:
118
+          - kuryr-tempest-plugin
117 119
         (...)
118 120
 
119 121
 Job variables
... ...
@@ -88,3 +88,12 @@ Write the local.conf file for use by devstack
88 88
    If a plugin declares a dependency on another plugin (via
89 89
    ``plugin_requires`` in the plugin's settings file), this role will
90 90
    automatically emit ``enable_plugin`` lines in the correct order.
91
+
92
+.. zuul:rolevar:: tempest_plugins
93
+   :type: list
94
+
95
+   A list of tempest plugins which are installed alongside tempest.
96
+
97
+   The list of values will be combined with the base devstack directory
98
+   and used to populate the ``TEMPEST_PLUGINS`` variable. If the variable
99
+   already exists, its value is *not* changed.
... ...
@@ -207,13 +207,15 @@ class PluginGraph(DependencyGraph):
207 207
 class LocalConf(object):
208 208
 
209 209
     def __init__(self, localrc, localconf, base_services, services, plugins,
210
-                 base_dir, projects, project):
210
+                 base_dir, projects, project, tempest_plugins):
211 211
         self.localrc = []
212
+        self.warnings = []
212 213
         self.meta_sections = {}
213 214
         self.plugin_deps = {}
214 215
         self.base_dir = base_dir
215 216
         self.projects = projects
216 217
         self.project = project
218
+        self.tempest_plugins = tempest_plugins
217 219
         if services or base_services:
218 220
             self.handle_services(base_services, services or {})
219 221
         self.handle_localrc(localrc)
... ...
@@ -246,12 +248,15 @@ class LocalConf(object):
246 246
 
247 247
     def handle_localrc(self, localrc):
248 248
         lfg = False
249
+        tp = False
249 250
         if localrc:
250 251
             vg = VarGraph(localrc)
251 252
             for k, v in vg.getVars():
252 253
                 self.localrc.append('{}={}'.format(k, v))
253 254
                 if k == 'LIBS_FROM_GIT':
254 255
                     lfg = True
256
+                elif k == 'TEMPEST_PLUGINS':
257
+                    tp = True
255 258
 
256 259
         if not lfg and (self.projects or self.project):
257 260
             required_projects = []
... ...
@@ -266,6 +271,19 @@ class LocalConf(object):
266 266
                 self.localrc.append('LIBS_FROM_GIT={}'.format(
267 267
                     ','.join(required_projects)))
268 268
 
269
+        if self.tempest_plugins:
270
+            if not tp:
271
+                tp_dirs = []
272
+                for tempest_plugin in self.tempest_plugins:
273
+                    tp_dirs.append(os.path.join(self.base_dir, tempest_plugin))
274
+                self.localrc.append('TEMPEST_PLUGINS="{}"'.format(
275
+                        ' '.join(tp_dirs)))
276
+            else:
277
+                self.warnings.append('TEMPEST_PLUGINS already defined ({}),'
278
+                                     'requested value {} ignored'.format(
279
+                                         tp, self.tempest_plugins))
280
+
281
+
269 282
     def handle_localconf(self, localconf):
270 283
         for phase, phase_data in localconf.items():
271 284
             for fn, fn_data in phase_data.items():
... ...
@@ -300,6 +318,7 @@ def main():
300 300
             path=dict(type='str'),
301 301
             projects=dict(type='dict'),
302 302
             project=dict(type='dict'),
303
+            tempest_plugins=dict(type='list'),
303 304
         )
304 305
     )
305 306
 
... ...
@@ -311,10 +330,11 @@ def main():
311 311
                    p.get('plugins'),
312 312
                    p.get('base_dir'),
313 313
                    p.get('projects'),
314
-                   p.get('project'))
314
+                   p.get('project'),
315
+                   p.get('tempest_plugins'))
315 316
     lc.write(p['path'])
316 317
 
317
-    module.exit_json()
318
+    module.exit_json(warnings=lc.warnings)
318 319
 
319 320
 
320 321
 try:
... ...
@@ -23,6 +23,20 @@ from devstack_local_conf import LocalConf
23 23
 from collections import OrderedDict
24 24
 
25 25
 class TestDevstackLocalConf(unittest.TestCase):
26
+
27
+    @staticmethod
28
+    def _init_localconf(p):
29
+        lc = LocalConf(p.get('localrc'),
30
+                       p.get('local_conf'),
31
+                       p.get('base_services'),
32
+                       p.get('services'),
33
+                       p.get('plugins'),
34
+                       p.get('base_dir'),
35
+                       p.get('projects'),
36
+                       p.get('project'),
37
+                       p.get('tempest_plugins'))
38
+        return lc
39
+
26 40
     def setUp(self):
27 41
         self.tmpdir = tempfile.mkdtemp()
28 42
 
... ...
@@ -51,14 +65,7 @@ class TestDevstackLocalConf(unittest.TestCase):
51 51
                  plugins=plugins,
52 52
                  base_dir='./test',
53 53
                  path=os.path.join(self.tmpdir, 'test.local.conf'))
54
-        lc = LocalConf(p.get('localrc'),
55
-                       p.get('local_conf'),
56
-                       p.get('base_services'),
57
-                       p.get('services'),
58
-                       p.get('plugins'),
59
-                       p.get('base_dir'),
60
-                       p.get('projects'),
61
-                       p.get('project'))
54
+        lc = self._init_localconf(p)
62 55
         lc.write(p['path'])
63 56
 
64 57
         plugins = []
... ...
@@ -104,14 +111,7 @@ class TestDevstackLocalConf(unittest.TestCase):
104 104
                  plugins=plugins,
105 105
                  base_dir=self.tmpdir,
106 106
                  path=os.path.join(self.tmpdir, 'test.local.conf'))
107
-        lc = LocalConf(p.get('localrc'),
108
-                       p.get('local_conf'),
109
-                       p.get('base_services'),
110
-                       p.get('services'),
111
-                       p.get('plugins'),
112
-                       p.get('base_dir'),
113
-                       p.get('projects'),
114
-                       p.get('project'))
107
+        lc = self._init_localconf(p)
115 108
         lc.write(p['path'])
116 109
 
117 110
         plugins = []
... ...
@@ -145,14 +145,7 @@ class TestDevstackLocalConf(unittest.TestCase):
145 145
                  path=os.path.join(self.tmpdir, 'test.local.conf'),
146 146
                  projects=projects,
147 147
                  project=project)
148
-        lc = LocalConf(p.get('localrc'),
149
-                       p.get('local_conf'),
150
-                       p.get('base_services'),
151
-                       p.get('services'),
152
-                       p.get('plugins'),
153
-                       p.get('base_dir'),
154
-                       p.get('projects'),
155
-                       p.get('project'))
148
+        lc = self._init_localconf(p)
156 149
         lc.write(p['path'])
157 150
 
158 151
         lfg = None
... ...
@@ -184,14 +177,7 @@ class TestDevstackLocalConf(unittest.TestCase):
184 184
                  base_dir='./test',
185 185
                  path=os.path.join(self.tmpdir, 'test.local.conf'),
186 186
                  projects=projects)
187
-        lc = LocalConf(p.get('localrc'),
188
-                       p.get('local_conf'),
189
-                       p.get('base_services'),
190
-                       p.get('services'),
191
-                       p.get('plugins'),
192
-                       p.get('base_dir'),
193
-                       p.get('projects'),
194
-                       p.get('project'))
187
+        lc = self._init_localconf(p)
195 188
         lc.write(p['path'])
196 189
 
197 190
         lfg = None
... ...
@@ -238,14 +224,50 @@ class TestDevstackLocalConf(unittest.TestCase):
238 238
                  base_dir=self.tmpdir,
239 239
                  path=os.path.join(self.tmpdir, 'test.local.conf'))
240 240
         with self.assertRaises(Exception):
241
-            lc = LocalConf(p.get('localrc'),
242
-                           p.get('local_conf'),
243
-                           p.get('base_services'),
244
-                           p.get('services'),
245
-                           p.get('plugins'),
246
-                           p.get('base_dir'))
241
+            lc = self._init_localconf(p)
247 242
             lc.write(p['path'])
248 243
 
244
+    def _find_tempest_plugins_value(self, file_path):
245
+        tp = None
246
+        with open(file_path) as f:
247
+            for line in f:
248
+                if line.startswith('TEMPEST_PLUGINS'):
249
+                    found = line.strip().split('=')[1]
250
+                    self.assertIsNone(tp,
251
+                        "TEMPEST_PLUGIN ({}) found again ({})".format(
252
+                            tp, found))
253
+                    tp = found
254
+        return tp
255
+
256
+    def test_tempest_plugins(self):
257
+        "Test that TEMPEST_PLUGINS is correctly populated."
258
+        p = dict(base_services=[],
259
+                 base_dir='./test',
260
+                 path=os.path.join(self.tmpdir, 'test.local.conf'),
261
+                 tempest_plugins=['heat-tempest-plugin', 'sahara-tests'])
262
+        lc = self._init_localconf(p)
263
+        lc.write(p['path'])
264
+
265
+        tp = self._find_tempest_plugins_value(p['path'])
266
+        self.assertEqual('"./test/heat-tempest-plugin ./test/sahara-tests"', tp)
267
+        self.assertEqual(len(lc.warnings), 0)
268
+
269
+    def test_tempest_plugins_not_overridden(self):
270
+        """Test that the existing value of TEMPEST_PLUGINS is not overridden
271
+        by the user-provided value, but a warning is emitted."""
272
+        localrc = {'TEMPEST_PLUGINS': 'someplugin'}
273
+        p = dict(localrc=localrc,
274
+                 base_services=[],
275
+                 base_dir='./test',
276
+                 path=os.path.join(self.tmpdir, 'test.local.conf'),
277
+                 tempest_plugins=['heat-tempest-plugin', 'sahara-tests'])
278
+        lc = self._init_localconf(p)
279
+        lc.write(p['path'])
280
+
281
+        tp = self._find_tempest_plugins_value(p['path'])
282
+        self.assertEqual('someplugin', tp)
283
+        self.assertEqual(len(lc.warnings), 1)
284
+
249 285
 
250 286
 if __name__ == '__main__':
251 287
     unittest.main()
... ...
@@ -10,4 +10,5 @@
10 10
     local_conf: "{{ devstack_local_conf|default(omit) }}"
11 11
     base_dir: "{{ devstack_base_dir|default(omit) }}"
12 12
     projects: "{{ zuul.projects }}"
13
-    project: "{{ zuul.project }}"
14 13
\ No newline at end of file
14
+    project: "{{ zuul.project }}"
15
+    tempest_plugins: "{{ tempest_plugins|default(omit) }}"