blob: 3538555facce61395dcf401e329623b67859487a [file] [log] [blame]
Maru Newby3fe5f852015-01-13 04:22:14 +00001# Copyright 2015 Red Hat, Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
Joshua Hesketh0aa7e8b2016-07-14 00:12:25 +100015
James E. Blairce8a2132016-05-19 15:21:52 -070016import os
17import random
18
19import fixtures
James E. Blair4317e9f2016-07-15 10:05:47 -070020import testtools
James E. Blairce8a2132016-05-19 15:21:52 -070021
Maru Newby3fe5f852015-01-13 04:22:14 +000022from zuul import model
James E. Blair83005782015-12-11 14:46:03 -080023from zuul import configloader
James E. Blairbf1a4f22017-03-17 10:59:37 -070024from zuul.lib import encryption
Clint Byrum88fdfde2017-03-28 16:22:42 -070025from zuul.lib import yamlutil as yaml
Maru Newby3fe5f852015-01-13 04:22:14 +000026
James E. Blair18f86a32017-03-15 14:43:26 -070027from tests.base import BaseTestCase, FIXTURE_DIR
Maru Newby3fe5f852015-01-13 04:22:14 +000028
29
James E. Blair0a899752017-03-29 13:22:16 -070030class Dummy(object):
31 def __init__(self, **kw):
32 for k, v in kw.items():
33 setattr(self, k, v)
James E. Blairb3f5db12017-03-17 12:57:39 -070034
35
Maru Newby3fe5f852015-01-13 04:22:14 +000036class TestJob(BaseTestCase):
James E. Blaira7f51ca2017-02-07 16:01:26 -080037 def setUp(self):
38 super(TestJob, self).setUp()
James E. Blair0a899752017-03-29 13:22:16 -070039 self.connection = Dummy(connection_name='dummy_connection')
40 self.source = Dummy(canonical_hostname='git.example.com',
James E. Blair0a899752017-03-29 13:22:16 -070041 connection=self.connection)
James E. Blairb3f5db12017-03-17 12:57:39 -070042 self.tenant = model.Tenant('tenant')
James E. Blair6459db12017-06-29 14:57:20 -070043 self.layout = model.Layout(self.tenant)
James E. Blair0a899752017-03-29 13:22:16 -070044 self.project = model.Project('project', self.source)
James E. Blair08d9b782017-06-29 14:22:48 -070045 self.tpc = model.TenantProjectConfig(self.project)
46 self.tenant.addUntrustedProject(self.tpc)
James E. Blairb3f5db12017-03-17 12:57:39 -070047 self.pipeline = model.Pipeline('gate', self.layout)
48 self.layout.addPipeline(self.pipeline)
49 self.queue = model.ChangeQueue(self.pipeline)
50
James E. Blair18f86a32017-03-15 14:43:26 -070051 private_key_file = os.path.join(FIXTURE_DIR, 'private.pem')
52 with open(private_key_file, "rb") as f:
James E. Blairbf1a4f22017-03-17 10:59:37 -070053 self.project.private_key, self.project.public_key = \
54 encryption.deserialize_rsa_keypair(f.read())
James E. Blair6f140c72017-03-03 10:32:07 -080055 self.context = model.SourceContext(self.project, 'master',
56 'test', True)
James E. Blair1cebebf2017-07-14 11:39:03 -070057 m = yaml.Mark('name', 0, 0, 0, '', 0)
58 self.start_mark = configloader.ZuulMark(m, m, '')
James E. Blaira7f51ca2017-02-07 16:01:26 -080059
Maru Newby3fe5f852015-01-13 04:22:14 +000060 @property
61 def job(self):
James E. Blair5ac93842017-01-20 06:47:34 -080062 tenant = model.Tenant('tenant')
James E. Blair6459db12017-06-29 14:57:20 -070063 layout = model.Layout(tenant)
James E. Blair5ac93842017-01-20 06:47:34 -080064 job = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -080065 '_source_context': self.context,
66 '_start_mark': self.start_mark,
James E. Blair83005782015-12-11 14:46:03 -080067 'name': 'job',
68 'irrelevant-files': [
69 '^docs/.*$'
70 ]})
Maru Newby3fe5f852015-01-13 04:22:14 +000071 return job
72
73 def test_change_matches_returns_false_for_matched_skip_if(self):
74 change = model.Change('project')
Alexander Evseevdbe6fab2015-11-19 12:46:34 +030075 change.files = ['/COMMIT_MSG', 'docs/foo']
Maru Newby3fe5f852015-01-13 04:22:14 +000076 self.assertFalse(self.job.changeMatches(change))
77
Jan Hruban570d01c2016-03-10 21:51:32 +010078 def test_change_matches_returns_false_for_single_matched_skip_if(self):
79 change = model.Change('project')
80 change.files = ['docs/foo']
81 self.assertFalse(self.job.changeMatches(change))
82
Maru Newby3fe5f852015-01-13 04:22:14 +000083 def test_change_matches_returns_true_for_unmatched_skip_if(self):
84 change = model.Change('project')
Alexander Evseevdbe6fab2015-11-19 12:46:34 +030085 change.files = ['/COMMIT_MSG', 'foo']
Maru Newby3fe5f852015-01-13 04:22:14 +000086 self.assertTrue(self.job.changeMatches(change))
87
Jan Hruban570d01c2016-03-10 21:51:32 +010088 def test_change_matches_returns_true_for_single_unmatched_skip_if(self):
89 change = model.Change('project')
90 change.files = ['foo']
91 self.assertTrue(self.job.changeMatches(change))
92
Maru Newby79427a42015-02-17 17:54:45 +000093 def test_job_sets_defaults_for_boolean_attributes(self):
James E. Blair83005782015-12-11 14:46:03 -080094 self.assertIsNotNone(self.job.voting)
95
96 def test_job_inheritance(self):
James E. Blaira7f51ca2017-02-07 16:01:26 -080097 # This is standard job inheritance.
98
James E. Blair74a82cf2017-07-12 17:23:08 -070099 base_pre = model.PlaybookContext(self.context, 'base-pre', [])
100 base_run = model.PlaybookContext(self.context, 'base-run', [])
101 base_post = model.PlaybookContext(self.context, 'base-post', [])
James E. Blaira7f51ca2017-02-07 16:01:26 -0800102
103 base = model.Job('base')
104 base.timeout = 30
105 base.pre_run = [base_pre]
106 base.run = [base_run]
107 base.post_run = [base_post]
James E. Blair8525e2b2017-03-15 14:05:47 -0700108 base.auth = model.AuthContext()
James E. Blaira7f51ca2017-02-07 16:01:26 -0800109
110 py27 = model.Job('py27')
Monty Taylor38b553a2017-06-05 13:06:10 -0500111 self.assertIsNone(py27.timeout)
James E. Blaira7f51ca2017-02-07 16:01:26 -0800112 py27.inheritFrom(base)
113 self.assertEqual(30, py27.timeout)
114 self.assertEqual(['base-pre'],
115 [x.path for x in py27.pre_run])
116 self.assertEqual(['base-run'],
117 [x.path for x in py27.run])
118 self.assertEqual(['base-post'],
119 [x.path for x in py27.post_run])
Monty Taylor38b553a2017-06-05 13:06:10 -0500120 self.assertIsNone(py27.auth)
James E. Blaira7f51ca2017-02-07 16:01:26 -0800121
122 def test_job_variants(self):
123 # This simulates freezing a job.
124
James E. Blair74a82cf2017-07-12 17:23:08 -0700125 py27_pre = model.PlaybookContext(self.context, 'py27-pre', [])
126 py27_run = model.PlaybookContext(self.context, 'py27-run', [])
127 py27_post = model.PlaybookContext(self.context, 'py27-post', [])
James E. Blaira7f51ca2017-02-07 16:01:26 -0800128
129 py27 = model.Job('py27')
130 py27.timeout = 30
131 py27.pre_run = [py27_pre]
132 py27.run = [py27_run]
133 py27.post_run = [py27_post]
James E. Blair8525e2b2017-03-15 14:05:47 -0700134 auth = model.AuthContext()
135 auth.secrets.append('foo')
James E. Blaira7f51ca2017-02-07 16:01:26 -0800136 py27.auth = auth
137
138 job = py27.copy()
139 self.assertEqual(30, job.timeout)
140
141 # Apply the diablo variant
142 diablo = model.Job('py27')
143 diablo.timeout = 40
144 job.applyVariant(diablo)
145
146 self.assertEqual(40, job.timeout)
147 self.assertEqual(['py27-pre'],
148 [x.path for x in job.pre_run])
149 self.assertEqual(['py27-run'],
150 [x.path for x in job.run])
151 self.assertEqual(['py27-post'],
152 [x.path for x in job.post_run])
153 self.assertEqual(auth, job.auth)
154
155 # Set the job to final for the following checks
156 job.final = True
157 self.assertTrue(job.voting)
158
159 good_final = model.Job('py27')
160 good_final.voting = False
161 job.applyVariant(good_final)
162 self.assertFalse(job.voting)
163
164 bad_final = model.Job('py27')
165 bad_final.timeout = 600
166 with testtools.ExpectedException(
167 Exception,
168 "Unable to modify final job"):
169 job.applyVariant(bad_final)
170
171 def test_job_inheritance_configloader(self):
172 # TODO(jeblair): move this to a configloader test
James E. Blair5ac93842017-01-20 06:47:34 -0800173 tenant = model.Tenant('tenant')
James E. Blair6459db12017-06-29 14:57:20 -0700174 layout = model.Layout(tenant)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700175
176 pipeline = model.Pipeline('gate', layout)
177 layout.addPipeline(pipeline)
178 queue = model.ChangeQueue(pipeline)
James E. Blair0a899752017-03-29 13:22:16 -0700179 project = model.Project('project', self.source)
James E. Blair08d9b782017-06-29 14:22:48 -0700180 tpc = model.TenantProjectConfig(project)
181 tenant.addUntrustedProject(tpc)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700182
James E. Blair5ac93842017-01-20 06:47:34 -0800183 base = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800184 '_source_context': self.context,
185 '_start_mark': self.start_mark,
James E. Blair83005782015-12-11 14:46:03 -0800186 'name': 'base',
187 'timeout': 30,
James E. Blaira7f51ca2017-02-07 16:01:26 -0800188 'pre-run': 'base-pre',
189 'post-run': 'base-post',
James E. Blair1774dd52017-02-03 10:52:32 -0800190 'nodes': [{
191 'name': 'controller',
James E. Blair16d96a02017-06-08 11:32:56 -0700192 'label': 'base',
James E. Blair1774dd52017-02-03 10:52:32 -0800193 }],
James E. Blair83005782015-12-11 14:46:03 -0800194 })
195 layout.addJob(base)
James E. Blair5ac93842017-01-20 06:47:34 -0800196 python27 = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800197 '_source_context': self.context,
198 '_start_mark': self.start_mark,
James E. Blair83005782015-12-11 14:46:03 -0800199 'name': 'python27',
200 'parent': 'base',
James E. Blaira7f51ca2017-02-07 16:01:26 -0800201 'pre-run': 'py27-pre',
202 'post-run': 'py27-post',
James E. Blair1774dd52017-02-03 10:52:32 -0800203 'nodes': [{
204 'name': 'controller',
James E. Blair16d96a02017-06-08 11:32:56 -0700205 'label': 'new',
James E. Blair1774dd52017-02-03 10:52:32 -0800206 }],
James E. Blair83005782015-12-11 14:46:03 -0800207 'timeout': 40,
208 })
209 layout.addJob(python27)
James E. Blair5ac93842017-01-20 06:47:34 -0800210 python27diablo = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800211 '_source_context': self.context,
212 '_start_mark': self.start_mark,
James E. Blair83005782015-12-11 14:46:03 -0800213 'name': 'python27',
214 'branches': [
215 'stable/diablo'
216 ],
James E. Blaira7f51ca2017-02-07 16:01:26 -0800217 'pre-run': 'py27-diablo-pre',
218 'run': 'py27-diablo',
219 'post-run': 'py27-diablo-post',
James E. Blair1774dd52017-02-03 10:52:32 -0800220 'nodes': [{
221 'name': 'controller',
James E. Blair16d96a02017-06-08 11:32:56 -0700222 'label': 'old',
James E. Blair1774dd52017-02-03 10:52:32 -0800223 }],
James E. Blair83005782015-12-11 14:46:03 -0800224 'timeout': 50,
225 })
226 layout.addJob(python27diablo)
227
James E. Blair5ac93842017-01-20 06:47:34 -0800228 python27essex = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800229 '_source_context': self.context,
230 '_start_mark': self.start_mark,
James E. Blaira7f51ca2017-02-07 16:01:26 -0800231 'name': 'python27',
232 'branches': [
233 'stable/essex'
234 ],
235 'pre-run': 'py27-essex-pre',
236 'post-run': 'py27-essex-post',
237 })
238 layout.addJob(python27essex)
239
James E. Blairff555742017-02-19 11:34:27 -0800240 project_config = configloader.ProjectParser.fromYaml(tenant, layout, [{
James E. Blairec7ff302017-03-04 07:31:32 -0800241 '_source_context': self.context,
242 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700243 'name': 'project',
244 'gate': {
245 'jobs': [
246 'python27'
247 ]
248 }
James E. Blairff555742017-02-19 11:34:27 -0800249 }])
James E. Blairf59f3cf2017-02-19 14:50:26 -0800250 layout.addProjectConfig(project_config)
James E. Blair83005782015-12-11 14:46:03 -0800251
James E. Blair83005782015-12-11 14:46:03 -0800252 change = model.Change(project)
James E. Blair1774dd52017-02-03 10:52:32 -0800253 # Test master
James E. Blair83005782015-12-11 14:46:03 -0800254 change.branch = 'master'
255 item = queue.enqueueChange(change)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700256 item.current_build_set.layout = layout
James E. Blair83005782015-12-11 14:46:03 -0800257
258 self.assertTrue(base.changeMatches(change))
259 self.assertTrue(python27.changeMatches(change))
260 self.assertFalse(python27diablo.changeMatches(change))
James E. Blaira7f51ca2017-02-07 16:01:26 -0800261 self.assertFalse(python27essex.changeMatches(change))
James E. Blair83005782015-12-11 14:46:03 -0800262
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200263 item.freezeJobGraph()
James E. Blair83005782015-12-11 14:46:03 -0800264 self.assertEqual(len(item.getJobs()), 1)
265 job = item.getJobs()[0]
266 self.assertEqual(job.name, 'python27')
267 self.assertEqual(job.timeout, 40)
James E. Blair1774dd52017-02-03 10:52:32 -0800268 nodes = job.nodeset.getNodes()
269 self.assertEqual(len(nodes), 1)
James E. Blair16d96a02017-06-08 11:32:56 -0700270 self.assertEqual(nodes[0].label, 'new')
James E. Blaira7f51ca2017-02-07 16:01:26 -0800271 self.assertEqual([x.path for x in job.pre_run],
Tobias Henkel165450e2017-06-26 22:53:45 +0200272 ['base-pre',
273 'py27-pre'])
James E. Blaira7f51ca2017-02-07 16:01:26 -0800274 self.assertEqual([x.path for x in job.post_run],
Tobias Henkel165450e2017-06-26 22:53:45 +0200275 ['py27-post',
276 'base-post'])
James E. Blaira7f51ca2017-02-07 16:01:26 -0800277 self.assertEqual([x.path for x in job.run],
278 ['playbooks/python27',
279 'playbooks/base'])
James E. Blair83005782015-12-11 14:46:03 -0800280
James E. Blair1774dd52017-02-03 10:52:32 -0800281 # Test diablo
James E. Blair83005782015-12-11 14:46:03 -0800282 change.branch = 'stable/diablo'
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700283 item = queue.enqueueChange(change)
284 item.current_build_set.layout = layout
James E. Blair83005782015-12-11 14:46:03 -0800285
286 self.assertTrue(base.changeMatches(change))
287 self.assertTrue(python27.changeMatches(change))
288 self.assertTrue(python27diablo.changeMatches(change))
James E. Blaira7f51ca2017-02-07 16:01:26 -0800289 self.assertFalse(python27essex.changeMatches(change))
James E. Blair83005782015-12-11 14:46:03 -0800290
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200291 item.freezeJobGraph()
James E. Blair83005782015-12-11 14:46:03 -0800292 self.assertEqual(len(item.getJobs()), 1)
293 job = item.getJobs()[0]
294 self.assertEqual(job.name, 'python27')
295 self.assertEqual(job.timeout, 50)
James E. Blair1774dd52017-02-03 10:52:32 -0800296 nodes = job.nodeset.getNodes()
297 self.assertEqual(len(nodes), 1)
James E. Blair16d96a02017-06-08 11:32:56 -0700298 self.assertEqual(nodes[0].label, 'old')
James E. Blaira7f51ca2017-02-07 16:01:26 -0800299 self.assertEqual([x.path for x in job.pre_run],
Tobias Henkel165450e2017-06-26 22:53:45 +0200300 ['base-pre',
301 'py27-pre',
302 'py27-diablo-pre'])
James E. Blaira7f51ca2017-02-07 16:01:26 -0800303 self.assertEqual([x.path for x in job.post_run],
Tobias Henkel165450e2017-06-26 22:53:45 +0200304 ['py27-diablo-post',
305 'py27-post',
306 'base-post'])
James E. Blaira7f51ca2017-02-07 16:01:26 -0800307 self.assertEqual([x.path for x in job.run],
Tobias Henkel165450e2017-06-26 22:53:45 +0200308 ['py27-diablo']),
James E. Blaira7f51ca2017-02-07 16:01:26 -0800309
310 # Test essex
311 change.branch = 'stable/essex'
312 item = queue.enqueueChange(change)
313 item.current_build_set.layout = layout
314
315 self.assertTrue(base.changeMatches(change))
316 self.assertTrue(python27.changeMatches(change))
317 self.assertFalse(python27diablo.changeMatches(change))
318 self.assertTrue(python27essex.changeMatches(change))
319
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200320 item.freezeJobGraph()
James E. Blaira7f51ca2017-02-07 16:01:26 -0800321 self.assertEqual(len(item.getJobs()), 1)
322 job = item.getJobs()[0]
323 self.assertEqual(job.name, 'python27')
324 self.assertEqual([x.path for x in job.pre_run],
Tobias Henkel165450e2017-06-26 22:53:45 +0200325 ['base-pre',
326 'py27-pre',
327 'py27-essex-pre'])
James E. Blaira7f51ca2017-02-07 16:01:26 -0800328 self.assertEqual([x.path for x in job.post_run],
Tobias Henkel165450e2017-06-26 22:53:45 +0200329 ['py27-essex-post',
330 'py27-post',
331 'base-post'])
James E. Blaira7f51ca2017-02-07 16:01:26 -0800332 self.assertEqual([x.path for x in job.run],
333 ['playbooks/python27',
334 'playbooks/base'])
James E. Blairce8a2132016-05-19 15:21:52 -0700335
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000336 def test_job_auth_inheritance(self):
James E. Blair6459db12017-06-29 14:57:20 -0700337 tenant = self.tenant
338 layout = self.layout
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000339
James E. Blair01f83b72017-03-15 13:03:40 -0700340 conf = yaml.safe_load('''
341- secret:
342 name: pypi-credentials
343 data:
344 username: test-username
James E. Blair9118c012017-08-03 11:19:16 -0700345 longpassword: !encrypted/pkcs1-oaep
346 - BFhtdnm8uXx7kn79RFL/zJywmzLkT1GY78P3bOtp4WghUFWobkifSu7ZpaV4NeO0s71Y
347 Usi1wGZZL0LveZjUN0t6OU1VZKSG8R5Ly7urjaSo1pPVIq5Rtt/H7W14Lecd+cUeKb4j
348 oeusC9drN3AA8a4oykcVpt1wVqUnTbMGC9ARMCQP6eopcs1l7tzMseprW4RDNhIuz3CR
349 gd0QBMPl6VDoFgBPB8vxtJw+3m0rqBYZCLZgCXekqlny8s2s92nJMuUABbJOEcDRarzi
350 bDsSXsfJt1y+5n7yOURsC7lovMg4GF/vCl/0YMKjBO5bpv9EM5fToeKYyPGSKQoHOnCY
351 ceb3cAVcv5UawcCic8XjhEhp4K7WPdYf2HVAC/qtxhbpjTxG4U5Q/SoppOJ60WqEkQvb
352 Xs6n5Dvy7xmph6GWmU/bAv3eUK3pdD3xa2Ue1lHWz3U+rsYraI+AKYsMYx3RBlfAmCeC
353 1ve2BXPrqnOo7G8tnUvfdYPbK4Aakk0ds/AVqFHEZN+S6hRBmBjLaRFWZ3QSO1NjbBxW
354 naHKZYT7nkrJm8AMCgZU0ZArFLpaufKCeiK5ECSsDxic4FIsY1OkWT42qEUfL0Wd+150
355 AKGNZpPJnnP3QYY4W/MWcKH/zdO400+zWN52WevbSqZy90tqKDJrBkMl1ydqbuw1E4ZH
356 vIs=
357 - BFhtdnm8uXx7kn79RFL/zJywmzLkT1GY78P3bOtp4WghUFWobkifSu7ZpaV4NeO0s71Y
358 Usi1wGZZL0LveZjUN0t6OU1VZKSG8R5Ly7urjaSo1pPVIq5Rtt/H7W14Lecd+cUeKb4j
359 oeusC9drN3AA8a4oykcVpt1wVqUnTbMGC9ARMCQP6eopcs1l7tzMseprW4RDNhIuz3CR
360 gd0QBMPl6VDoFgBPB8vxtJw+3m0rqBYZCLZgCXekqlny8s2s92nJMuUABbJOEcDRarzi
361 bDsSXsfJt1y+5n7yOURsC7lovMg4GF/vCl/0YMKjBO5bpv9EM5fToeKYyPGSKQoHOnCY
362 ceb3cAVcv5UawcCic8XjhEhp4K7WPdYf2HVAC/qtxhbpjTxG4U5Q/SoppOJ60WqEkQvb
363 Xs6n5Dvy7xmph6GWmU/bAv3eUK3pdD3xa2Ue1lHWz3U+rsYraI+AKYsMYx3RBlfAmCeC
364 1ve2BXPrqnOo7G8tnUvfdYPbK4Aakk0ds/AVqFHEZN+S6hRBmBjLaRFWZ3QSO1NjbBxW
365 naHKZYT7nkrJm8AMCgZU0ZArFLpaufKCeiK5ECSsDxic4FIsY1OkWT42qEUfL0Wd+150
366 AKGNZpPJnnP3QYY4W/MWcKH/zdO400+zWN52WevbSqZy90tqKDJrBkMl1ydqbuw1E4ZH
367 vIs=
James E. Blair717e8e92017-03-17 11:03:27 -0700368 password: !encrypted/pkcs1-oaep |
James E. Blair9118c012017-08-03 11:19:16 -0700369 BFhtdnm8uXx7kn79RFL/zJywmzLkT1GY78P3bOtp4WghUFWobkifSu7ZpaV4NeO0s71Y
370 Usi1wGZZL0LveZjUN0t6OU1VZKSG8R5Ly7urjaSo1pPVIq5Rtt/H7W14Lecd+cUeKb4j
371 oeusC9drN3AA8a4oykcVpt1wVqUnTbMGC9ARMCQP6eopcs1l7tzMseprW4RDNhIuz3CR
372 gd0QBMPl6VDoFgBPB8vxtJw+3m0rqBYZCLZgCXekqlny8s2s92nJMuUABbJOEcDRarzi
373 bDsSXsfJt1y+5n7yOURsC7lovMg4GF/vCl/0YMKjBO5bpv9EM5fToeKYyPGSKQoHOnCY
374 ceb3cAVcv5UawcCic8XjhEhp4K7WPdYf2HVAC/qtxhbpjTxG4U5Q/SoppOJ60WqEkQvb
375 Xs6n5Dvy7xmph6GWmU/bAv3eUK3pdD3xa2Ue1lHWz3U+rsYraI+AKYsMYx3RBlfAmCeC
376 1ve2BXPrqnOo7G8tnUvfdYPbK4Aakk0ds/AVqFHEZN+S6hRBmBjLaRFWZ3QSO1NjbBxW
377 naHKZYT7nkrJm8AMCgZU0ZArFLpaufKCeiK5ECSsDxic4FIsY1OkWT42qEUfL0Wd+150
378 AKGNZpPJnnP3QYY4W/MWcKH/zdO400+zWN52WevbSqZy90tqKDJrBkMl1ydqbuw1E4ZH
379 vIs=
James E. Blair01f83b72017-03-15 13:03:40 -0700380''')[0]['secret']
381
382 conf['_source_context'] = self.context
383 conf['_start_mark'] = self.start_mark
384
385 secret = configloader.SecretParser.fromYaml(layout, conf)
386 layout.addSecret(secret)
387
James E. Blair6459db12017-06-29 14:57:20 -0700388 base = configloader.JobParser.fromYaml(self.tenant, self.layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800389 '_source_context': self.context,
390 '_start_mark': self.start_mark,
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000391 'name': 'base',
392 'timeout': 30,
393 })
394 layout.addJob(base)
James E. Blair5ac93842017-01-20 06:47:34 -0800395 pypi_upload_without_inherit = configloader.JobParser.fromYaml(
396 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800397 '_source_context': self.context,
398 '_start_mark': self.start_mark,
James E. Blair5ac93842017-01-20 06:47:34 -0800399 'name': 'pypi-upload-without-inherit',
400 'parent': 'base',
401 'timeout': 40,
402 'auth': {
403 'secrets': [
404 'pypi-credentials',
405 ]
406 }
407 })
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000408 layout.addJob(pypi_upload_without_inherit)
James E. Blair5ac93842017-01-20 06:47:34 -0800409 pypi_upload_with_inherit = configloader.JobParser.fromYaml(
410 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800411 '_source_context': self.context,
412 '_start_mark': self.start_mark,
James E. Blair5ac93842017-01-20 06:47:34 -0800413 'name': 'pypi-upload-with-inherit',
414 'parent': 'base',
415 'timeout': 40,
416 'auth': {
417 'inherit': True,
418 'secrets': [
419 'pypi-credentials',
420 ]
421 }
422 })
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000423 layout.addJob(pypi_upload_with_inherit)
424 pypi_upload_with_inherit_false = configloader.JobParser.fromYaml(
James E. Blair5ac93842017-01-20 06:47:34 -0800425 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800426 '_source_context': self.context,
427 '_start_mark': self.start_mark,
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000428 'name': 'pypi-upload-with-inherit-false',
429 'parent': 'base',
430 'timeout': 40,
431 'auth': {
432 'inherit': False,
Ricardo Carrillo Cruz12c892b2016-11-18 15:35:49 +0000433 'secrets': [
434 'pypi-credentials',
435 ]
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000436 }
437 })
438 layout.addJob(pypi_upload_with_inherit_false)
James E. Blair5ac93842017-01-20 06:47:34 -0800439 in_repo_job_without_inherit = configloader.JobParser.fromYaml(
440 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800441 '_source_context': self.context,
442 '_start_mark': self.start_mark,
James E. Blair5ac93842017-01-20 06:47:34 -0800443 'name': 'in-repo-job-without-inherit',
444 'parent': 'pypi-upload-without-inherit',
445 })
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000446 layout.addJob(in_repo_job_without_inherit)
James E. Blair5ac93842017-01-20 06:47:34 -0800447 in_repo_job_with_inherit = configloader.JobParser.fromYaml(
448 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800449 '_source_context': self.context,
450 '_start_mark': self.start_mark,
James E. Blair5ac93842017-01-20 06:47:34 -0800451 'name': 'in-repo-job-with-inherit',
452 'parent': 'pypi-upload-with-inherit',
453 })
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000454 layout.addJob(in_repo_job_with_inherit)
455 in_repo_job_with_inherit_false = configloader.JobParser.fromYaml(
James E. Blair5ac93842017-01-20 06:47:34 -0800456 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800457 '_source_context': self.context,
458 '_start_mark': self.start_mark,
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000459 'name': 'in-repo-job-with-inherit-false',
460 'parent': 'pypi-upload-with-inherit-false',
461 })
462 layout.addJob(in_repo_job_with_inherit_false)
463
Monty Taylor38b553a2017-06-05 13:06:10 -0500464 self.assertIsNone(in_repo_job_without_inherit.auth)
James E. Blair8525e2b2017-03-15 14:05:47 -0700465 self.assertEqual(1, len(in_repo_job_with_inherit.auth.secrets))
466 self.assertEqual(in_repo_job_with_inherit.auth.secrets[0].name,
467 'pypi-credentials')
Monty Taylor38b553a2017-06-05 13:06:10 -0500468 self.assertIsNone(in_repo_job_with_inherit_false.auth)
James E. Blair9118c012017-08-03 11:19:16 -0700469 self.assertEqual(in_repo_job_with_inherit.auth.secrets[0].
470 secret_data['longpassword'],
471 'test-passwordtest-password')
472 self.assertEqual(in_repo_job_with_inherit.auth.secrets[0].
473 secret_data['password'],
474 'test-password')
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000475
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700476 def test_job_inheritance_job_tree(self):
James E. Blair5ac93842017-01-20 06:47:34 -0800477 tenant = model.Tenant('tenant')
James E. Blair6459db12017-06-29 14:57:20 -0700478 layout = model.Layout(tenant)
James E. Blair08d9b782017-06-29 14:22:48 -0700479 tpc = model.TenantProjectConfig(self.project)
480 tenant.addUntrustedProject(tpc)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700481
482 pipeline = model.Pipeline('gate', layout)
483 layout.addPipeline(pipeline)
484 queue = model.ChangeQueue(pipeline)
485
James E. Blair5ac93842017-01-20 06:47:34 -0800486 base = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800487 '_source_context': self.context,
488 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700489 'name': 'base',
490 'timeout': 30,
491 })
492 layout.addJob(base)
James E. Blair5ac93842017-01-20 06:47:34 -0800493 python27 = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800494 '_source_context': self.context,
495 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700496 'name': 'python27',
497 'parent': 'base',
498 'timeout': 40,
499 })
500 layout.addJob(python27)
James E. Blair5ac93842017-01-20 06:47:34 -0800501 python27diablo = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800502 '_source_context': self.context,
503 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700504 'name': 'python27',
505 'branches': [
506 'stable/diablo'
507 ],
508 'timeout': 50,
509 })
510 layout.addJob(python27diablo)
511
James E. Blairff555742017-02-19 11:34:27 -0800512 project_config = configloader.ProjectParser.fromYaml(tenant, layout, [{
James E. Blairec7ff302017-03-04 07:31:32 -0800513 '_source_context': self.context,
514 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700515 'name': 'project',
516 'gate': {
517 'jobs': [
518 {'python27': {'timeout': 70}}
519 ]
520 }
James E. Blairff555742017-02-19 11:34:27 -0800521 }])
James E. Blairf59f3cf2017-02-19 14:50:26 -0800522 layout.addProjectConfig(project_config)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700523
James E. Blairec7ff302017-03-04 07:31:32 -0800524 change = model.Change(self.project)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700525 change.branch = 'master'
526 item = queue.enqueueChange(change)
527 item.current_build_set.layout = layout
528
529 self.assertTrue(base.changeMatches(change))
530 self.assertTrue(python27.changeMatches(change))
531 self.assertFalse(python27diablo.changeMatches(change))
532
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200533 item.freezeJobGraph()
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700534 self.assertEqual(len(item.getJobs()), 1)
535 job = item.getJobs()[0]
536 self.assertEqual(job.name, 'python27')
537 self.assertEqual(job.timeout, 70)
538
539 change.branch = 'stable/diablo'
540 item = queue.enqueueChange(change)
541 item.current_build_set.layout = layout
542
543 self.assertTrue(base.changeMatches(change))
544 self.assertTrue(python27.changeMatches(change))
545 self.assertTrue(python27diablo.changeMatches(change))
546
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200547 item.freezeJobGraph()
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700548 self.assertEqual(len(item.getJobs()), 1)
549 job = item.getJobs()[0]
550 self.assertEqual(job.name, 'python27')
551 self.assertEqual(job.timeout, 70)
552
Clint Byrum85493602016-11-18 11:59:47 -0800553 def test_inheritance_keeps_matchers(self):
James E. Blair5ac93842017-01-20 06:47:34 -0800554 tenant = model.Tenant('tenant')
James E. Blair6459db12017-06-29 14:57:20 -0700555 layout = model.Layout(tenant)
Clint Byrum85493602016-11-18 11:59:47 -0800556
557 pipeline = model.Pipeline('gate', layout)
558 layout.addPipeline(pipeline)
559 queue = model.ChangeQueue(pipeline)
James E. Blair0a899752017-03-29 13:22:16 -0700560 project = model.Project('project', self.source)
James E. Blair08d9b782017-06-29 14:22:48 -0700561 tpc = model.TenantProjectConfig(project)
562 tenant.addUntrustedProject(tpc)
Clint Byrum85493602016-11-18 11:59:47 -0800563
James E. Blair5ac93842017-01-20 06:47:34 -0800564 base = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800565 '_source_context': self.context,
566 '_start_mark': self.start_mark,
Clint Byrum85493602016-11-18 11:59:47 -0800567 'name': 'base',
568 'timeout': 30,
569 })
570 layout.addJob(base)
James E. Blair5ac93842017-01-20 06:47:34 -0800571 python27 = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800572 '_source_context': self.context,
573 '_start_mark': self.start_mark,
Clint Byrum85493602016-11-18 11:59:47 -0800574 'name': 'python27',
575 'parent': 'base',
576 'timeout': 40,
577 'irrelevant-files': ['^ignored-file$'],
578 })
579 layout.addJob(python27)
580
James E. Blairff555742017-02-19 11:34:27 -0800581 project_config = configloader.ProjectParser.fromYaml(tenant, layout, [{
James E. Blairec7ff302017-03-04 07:31:32 -0800582 '_source_context': self.context,
583 '_start_mark': self.start_mark,
Clint Byrum85493602016-11-18 11:59:47 -0800584 'name': 'project',
585 'gate': {
586 'jobs': [
587 'python27',
588 ]
589 }
James E. Blairff555742017-02-19 11:34:27 -0800590 }])
James E. Blairf59f3cf2017-02-19 14:50:26 -0800591 layout.addProjectConfig(project_config)
Clint Byrum85493602016-11-18 11:59:47 -0800592
593 change = model.Change(project)
594 change.branch = 'master'
595 change.files = ['/COMMIT_MSG', 'ignored-file']
596 item = queue.enqueueChange(change)
597 item.current_build_set.layout = layout
598
599 self.assertTrue(base.changeMatches(change))
600 self.assertFalse(python27.changeMatches(change))
601
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200602 item.freezeJobGraph()
Clint Byrum85493602016-11-18 11:59:47 -0800603 self.assertEqual([], item.getJobs())
604
James E. Blair4317e9f2016-07-15 10:05:47 -0700605 def test_job_source_project(self):
James E. Blair6459db12017-06-29 14:57:20 -0700606 tenant = self.tenant
607 layout = self.layout
James E. Blair0a899752017-03-29 13:22:16 -0700608 base_project = model.Project('base_project', self.source)
James E. Blair6f140c72017-03-03 10:32:07 -0800609 base_context = model.SourceContext(base_project, 'master',
610 'test', True)
James E. Blair6459db12017-06-29 14:57:20 -0700611 tpc = model.TenantProjectConfig(base_project)
612 tenant.addUntrustedProject(tpc)
James E. Blaircdab2032017-02-01 09:09:29 -0800613
James E. Blair5ac93842017-01-20 06:47:34 -0800614 base = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blaircdab2032017-02-01 09:09:29 -0800615 '_source_context': base_context,
James E. Blairec7ff302017-03-04 07:31:32 -0800616 '_start_mark': self.start_mark,
James E. Blair4317e9f2016-07-15 10:05:47 -0700617 'name': 'base',
618 })
619 layout.addJob(base)
620
James E. Blair0a899752017-03-29 13:22:16 -0700621 other_project = model.Project('other_project', self.source)
James E. Blair6f140c72017-03-03 10:32:07 -0800622 other_context = model.SourceContext(other_project, 'master',
623 'test', True)
James E. Blair6459db12017-06-29 14:57:20 -0700624 tpc = model.TenantProjectConfig(other_project)
625 tenant.addUntrustedProject(tpc)
James E. Blair5ac93842017-01-20 06:47:34 -0800626 base2 = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blaircdab2032017-02-01 09:09:29 -0800627 '_source_context': other_context,
James E. Blairec7ff302017-03-04 07:31:32 -0800628 '_start_mark': self.start_mark,
James E. Blair4317e9f2016-07-15 10:05:47 -0700629 'name': 'base',
630 })
631 with testtools.ExpectedException(
632 Exception,
633 "Job base in other_project is not permitted "
634 "to shadow job base in base_project"):
635 layout.addJob(base2)
636
James E. Blairb3f5db12017-03-17 12:57:39 -0700637 def test_job_allowed_projects(self):
638 job = configloader.JobParser.fromYaml(self.tenant, self.layout, {
639 '_source_context': self.context,
640 '_start_mark': self.start_mark,
641 'name': 'job',
642 'allowed-projects': ['project'],
643 })
644 self.layout.addJob(job)
645
James E. Blair0a899752017-03-29 13:22:16 -0700646 project2 = model.Project('project2', self.source)
James E. Blair08d9b782017-06-29 14:22:48 -0700647 tpc2 = model.TenantProjectConfig(project2)
648 self.tenant.addUntrustedProject(tpc2)
James E. Blairb3f5db12017-03-17 12:57:39 -0700649 context2 = model.SourceContext(project2, 'master',
650 'test', True)
651
652 project2_config = configloader.ProjectParser.fromYaml(
653 self.tenant, self.layout, [{
654 '_source_context': context2,
655 '_start_mark': self.start_mark,
656 'name': 'project2',
657 'gate': {
658 'jobs': [
659 'job'
660 ]
661 }
662 }]
663 )
664 self.layout.addProjectConfig(project2_config)
665
666 change = model.Change(project2)
667 # Test master
668 change.branch = 'master'
669 item = self.queue.enqueueChange(change)
670 item.current_build_set.layout = self.layout
671 with testtools.ExpectedException(
672 Exception,
673 "Project project2 is not allowed to run job job"):
674 item.freezeJobGraph()
675
James E. Blaird2348362017-03-17 13:59:35 -0700676 def test_job_pipeline_allow_secrets(self):
677 self.pipeline.allow_secrets = False
678 job = configloader.JobParser.fromYaml(self.tenant, self.layout, {
679 '_source_context': self.context,
680 '_start_mark': self.start_mark,
681 'name': 'job',
682 })
683 auth = model.AuthContext()
684 auth.secrets.append('foo')
685 job.auth = auth
686
687 self.layout.addJob(job)
688
689 project_config = configloader.ProjectParser.fromYaml(
690 self.tenant, self.layout, [{
691 '_source_context': self.context,
692 '_start_mark': self.start_mark,
693 'name': 'project',
694 'gate': {
695 'jobs': [
696 'job'
697 ]
698 }
699 }]
700 )
701 self.layout.addProjectConfig(project_config)
702
703 change = model.Change(self.project)
704 # Test master
705 change.branch = 'master'
706 item = self.queue.enqueueChange(change)
707 item.current_build_set.layout = self.layout
708 with testtools.ExpectedException(
709 Exception,
710 "Pipeline gate does not allow jobs with secrets"):
711 item.freezeJobGraph()
712
James E. Blairce8a2132016-05-19 15:21:52 -0700713
714class TestJobTimeData(BaseTestCase):
715 def setUp(self):
716 super(TestJobTimeData, self).setUp()
717 self.tmp_root = self.useFixture(fixtures.TempDir(
718 rootdir=os.environ.get("ZUUL_TEST_ROOT"))
719 ).path
720
721 def test_empty_timedata(self):
722 path = os.path.join(self.tmp_root, 'job-name')
723 self.assertFalse(os.path.exists(path))
724 self.assertFalse(os.path.exists(path + '.tmp'))
725 td = model.JobTimeData(path)
726 self.assertEqual(td.success_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
727 self.assertEqual(td.failure_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
728 self.assertEqual(td.results, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
729
730 def test_save_reload(self):
731 path = os.path.join(self.tmp_root, 'job-name')
732 self.assertFalse(os.path.exists(path))
733 self.assertFalse(os.path.exists(path + '.tmp'))
734 td = model.JobTimeData(path)
735 self.assertEqual(td.success_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
736 self.assertEqual(td.failure_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
737 self.assertEqual(td.results, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
738 success_times = []
739 failure_times = []
740 results = []
741 for x in range(10):
742 success_times.append(int(random.random() * 1000))
743 failure_times.append(int(random.random() * 1000))
744 results.append(0)
745 results.append(1)
746 random.shuffle(results)
747 s = f = 0
748 for result in results:
749 if result:
750 td.add(failure_times[f], 'FAILURE')
751 f += 1
752 else:
753 td.add(success_times[s], 'SUCCESS')
754 s += 1
755 self.assertEqual(td.success_times, success_times)
756 self.assertEqual(td.failure_times, failure_times)
757 self.assertEqual(td.results, results[10:])
758 td.save()
759 self.assertTrue(os.path.exists(path))
760 self.assertFalse(os.path.exists(path + '.tmp'))
761 td = model.JobTimeData(path)
762 td.load()
763 self.assertEqual(td.success_times, success_times)
764 self.assertEqual(td.failure_times, failure_times)
765 self.assertEqual(td.results, results[10:])
766
767
768class TestTimeDataBase(BaseTestCase):
769 def setUp(self):
770 super(TestTimeDataBase, self).setUp()
771 self.tmp_root = self.useFixture(fixtures.TempDir(
772 rootdir=os.environ.get("ZUUL_TEST_ROOT"))
773 ).path
774 self.db = model.TimeDataBase(self.tmp_root)
775
776 def test_timedatabase(self):
777 self.assertEqual(self.db.getEstimatedTime('job-name'), 0)
778 self.db.update('job-name', 50, 'SUCCESS')
779 self.assertEqual(self.db.getEstimatedTime('job-name'), 50)
780 self.db.update('job-name', 100, 'SUCCESS')
781 self.assertEqual(self.db.getEstimatedTime('job-name'), 75)
782 for x in range(10):
783 self.db.update('job-name', 100, 'SUCCESS')
784 self.assertEqual(self.db.getEstimatedTime('job-name'), 100)
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200785
786
787class TestGraph(BaseTestCase):
788 def test_job_graph_disallows_multiple_jobs_with_same_name(self):
789 graph = model.JobGraph()
790 job1 = model.Job('job')
791 job2 = model.Job('job')
792 graph.addJob(job1)
793 with testtools.ExpectedException(Exception,
794 "Job job already added"):
795 graph.addJob(job2)
796
797 def test_job_graph_disallows_circular_dependencies(self):
798 graph = model.JobGraph()
799 jobs = [model.Job('job%d' % i) for i in range(0, 10)]
800 prevjob = None
801 for j in jobs[:3]:
802 if prevjob:
803 j.dependencies = frozenset([prevjob.name])
804 graph.addJob(j)
805 prevjob = j
806 # 0 triggers 1 triggers 2 triggers 3...
807
808 # Cannot depend on itself
809 with testtools.ExpectedException(
810 Exception,
811 "Dependency cycle detected in job jobX"):
812 j = model.Job('jobX')
813 j.dependencies = frozenset([j.name])
814 graph.addJob(j)
815
816 # Disallow circular dependencies
817 with testtools.ExpectedException(
818 Exception,
819 "Dependency cycle detected in job job3"):
820 jobs[4].dependencies = frozenset([jobs[3].name])
821 graph.addJob(jobs[4])
822 jobs[3].dependencies = frozenset([jobs[4].name])
823 graph.addJob(jobs[3])
824
825 jobs[5].dependencies = frozenset([jobs[4].name])
826 graph.addJob(jobs[5])
827
828 with testtools.ExpectedException(
829 Exception,
830 "Dependency cycle detected in job job3"):
831 jobs[3].dependencies = frozenset([jobs[5].name])
832 graph.addJob(jobs[3])
833
834 jobs[3].dependencies = frozenset([jobs[2].name])
835 graph.addJob(jobs[3])
836 jobs[6].dependencies = frozenset([jobs[2].name])
837 graph.addJob(jobs[6])
James E. Blairc2a54fd2017-03-29 15:19:26 -0700838
839
840class TestTenant(BaseTestCase):
841 def test_add_project(self):
842 tenant = model.Tenant('tenant')
843 connection1 = Dummy(connection_name='dummy_connection1')
844 source1 = Dummy(canonical_hostname='git1.example.com',
845 name='dummy', # TODOv3(jeblair): remove
846 connection=connection1)
847
848 source1_project1 = model.Project('project1', source1)
James E. Blair08d9b782017-06-29 14:22:48 -0700849 source1_project1_tpc = model.TenantProjectConfig(source1_project1)
850 tenant.addConfigProject(source1_project1_tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700851 d = {'project1':
852 {'git1.example.com': source1_project1}}
853 self.assertEqual(d, tenant.projects)
854 self.assertEqual((True, source1_project1),
855 tenant.getProject('project1'))
856 self.assertEqual((True, source1_project1),
857 tenant.getProject('git1.example.com/project1'))
858
859 source1_project2 = model.Project('project2', source1)
James E. Blair08d9b782017-06-29 14:22:48 -0700860 tpc = model.TenantProjectConfig(source1_project2)
861 tenant.addUntrustedProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700862 d = {'project1':
863 {'git1.example.com': source1_project1},
864 'project2':
865 {'git1.example.com': source1_project2}}
866 self.assertEqual(d, tenant.projects)
867 self.assertEqual((False, source1_project2),
868 tenant.getProject('project2'))
869 self.assertEqual((False, source1_project2),
870 tenant.getProject('git1.example.com/project2'))
871
872 connection2 = Dummy(connection_name='dummy_connection2')
873 source2 = Dummy(canonical_hostname='git2.example.com',
874 name='dummy', # TODOv3(jeblair): remove
875 connection=connection2)
876
877 source2_project1 = model.Project('project1', source2)
James E. Blair08d9b782017-06-29 14:22:48 -0700878 tpc = model.TenantProjectConfig(source2_project1)
879 tenant.addUntrustedProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700880 d = {'project1':
881 {'git1.example.com': source1_project1,
882 'git2.example.com': source2_project1},
883 'project2':
884 {'git1.example.com': source1_project2}}
885 self.assertEqual(d, tenant.projects)
886 with testtools.ExpectedException(
887 Exception,
888 "Project name 'project1' is ambiguous"):
889 tenant.getProject('project1')
890 self.assertEqual((False, source1_project2),
891 tenant.getProject('project2'))
892 self.assertEqual((True, source1_project1),
893 tenant.getProject('git1.example.com/project1'))
894 self.assertEqual((False, source2_project1),
895 tenant.getProject('git2.example.com/project1'))
896
897 source2_project2 = model.Project('project2', source2)
James E. Blair08d9b782017-06-29 14:22:48 -0700898 tpc = model.TenantProjectConfig(source2_project2)
899 tenant.addConfigProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700900 d = {'project1':
901 {'git1.example.com': source1_project1,
902 'git2.example.com': source2_project1},
903 'project2':
904 {'git1.example.com': source1_project2,
905 'git2.example.com': source2_project2}}
906 self.assertEqual(d, tenant.projects)
907 with testtools.ExpectedException(
908 Exception,
909 "Project name 'project1' is ambiguous"):
910 tenant.getProject('project1')
911 with testtools.ExpectedException(
912 Exception,
913 "Project name 'project2' is ambiguous"):
914 tenant.getProject('project2')
915 self.assertEqual((True, source1_project1),
916 tenant.getProject('git1.example.com/project1'))
917 self.assertEqual((False, source2_project1),
918 tenant.getProject('git2.example.com/project1'))
919 self.assertEqual((False, source1_project2),
920 tenant.getProject('git1.example.com/project2'))
921 self.assertEqual((True, source2_project2),
922 tenant.getProject('git2.example.com/project2'))
923
924 source1_project2b = model.Project('subpath/project2', source1)
James E. Blair08d9b782017-06-29 14:22:48 -0700925 tpc = model.TenantProjectConfig(source1_project2b)
926 tenant.addConfigProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700927 d = {'project1':
928 {'git1.example.com': source1_project1,
929 'git2.example.com': source2_project1},
930 'project2':
931 {'git1.example.com': source1_project2,
932 'git2.example.com': source2_project2},
933 'subpath/project2':
934 {'git1.example.com': source1_project2b}}
935 self.assertEqual(d, tenant.projects)
936 self.assertEqual((False, source1_project2),
937 tenant.getProject('git1.example.com/project2'))
938 self.assertEqual((True, source2_project2),
939 tenant.getProject('git2.example.com/project2'))
940 self.assertEqual((True, source1_project2b),
941 tenant.getProject('subpath/project2'))
942 self.assertEqual(
943 (True, source1_project2b),
944 tenant.getProject('git1.example.com/subpath/project2'))
945
946 source2_project2b = model.Project('subpath/project2', source2)
James E. Blair08d9b782017-06-29 14:22:48 -0700947 tpc = model.TenantProjectConfig(source2_project2b)
948 tenant.addConfigProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700949 d = {'project1':
950 {'git1.example.com': source1_project1,
951 'git2.example.com': source2_project1},
952 'project2':
953 {'git1.example.com': source1_project2,
954 'git2.example.com': source2_project2},
955 'subpath/project2':
956 {'git1.example.com': source1_project2b,
957 'git2.example.com': source2_project2b}}
958 self.assertEqual(d, tenant.projects)
959 self.assertEqual((False, source1_project2),
960 tenant.getProject('git1.example.com/project2'))
961 self.assertEqual((True, source2_project2),
962 tenant.getProject('git2.example.com/project2'))
963 with testtools.ExpectedException(
964 Exception,
965 "Project name 'subpath/project2' is ambiguous"):
966 tenant.getProject('subpath/project2')
967 self.assertEqual(
968 (True, source1_project2b),
969 tenant.getProject('git1.example.com/subpath/project2'))
970 self.assertEqual(
971 (True, source2_project2b),
972 tenant.getProject('git2.example.com/subpath/project2'))
973
974 with testtools.ExpectedException(
975 Exception,
976 "Project project1 is already in project index"):
James E. Blair08d9b782017-06-29 14:22:48 -0700977 tenant._addProject(source1_project1_tpc)