blob: 6a63125e4d2d46ec449123c86174b349812e0318 [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',
James E. Blair2bab6e72017-08-07 09:52:45 -070068 'parent': None,
James E. Blair83005782015-12-11 14:46:03 -080069 'irrelevant-files': [
70 '^docs/.*$'
71 ]})
Maru Newby3fe5f852015-01-13 04:22:14 +000072 return job
73
74 def test_change_matches_returns_false_for_matched_skip_if(self):
75 change = model.Change('project')
Alexander Evseevdbe6fab2015-11-19 12:46:34 +030076 change.files = ['/COMMIT_MSG', 'docs/foo']
Maru Newby3fe5f852015-01-13 04:22:14 +000077 self.assertFalse(self.job.changeMatches(change))
78
Jan Hruban570d01c2016-03-10 21:51:32 +010079 def test_change_matches_returns_false_for_single_matched_skip_if(self):
80 change = model.Change('project')
81 change.files = ['docs/foo']
82 self.assertFalse(self.job.changeMatches(change))
83
Maru Newby3fe5f852015-01-13 04:22:14 +000084 def test_change_matches_returns_true_for_unmatched_skip_if(self):
85 change = model.Change('project')
Alexander Evseevdbe6fab2015-11-19 12:46:34 +030086 change.files = ['/COMMIT_MSG', 'foo']
Maru Newby3fe5f852015-01-13 04:22:14 +000087 self.assertTrue(self.job.changeMatches(change))
88
Jan Hruban570d01c2016-03-10 21:51:32 +010089 def test_change_matches_returns_true_for_single_unmatched_skip_if(self):
90 change = model.Change('project')
91 change.files = ['foo']
92 self.assertTrue(self.job.changeMatches(change))
93
Maru Newby79427a42015-02-17 17:54:45 +000094 def test_job_sets_defaults_for_boolean_attributes(self):
James E. Blair83005782015-12-11 14:46:03 -080095 self.assertIsNotNone(self.job.voting)
96
97 def test_job_inheritance(self):
James E. Blaira7f51ca2017-02-07 16:01:26 -080098 # This is standard job inheritance.
99
James E. Blair74a82cf2017-07-12 17:23:08 -0700100 base_pre = model.PlaybookContext(self.context, 'base-pre', [])
101 base_run = model.PlaybookContext(self.context, 'base-run', [])
102 base_post = model.PlaybookContext(self.context, 'base-post', [])
James E. Blaira7f51ca2017-02-07 16:01:26 -0800103
104 base = model.Job('base')
105 base.timeout = 30
106 base.pre_run = [base_pre]
107 base.run = [base_run]
108 base.post_run = [base_post]
James E. Blair8525e2b2017-03-15 14:05:47 -0700109 base.auth = model.AuthContext()
James E. Blaira7f51ca2017-02-07 16:01:26 -0800110
111 py27 = model.Job('py27')
Monty Taylor38b553a2017-06-05 13:06:10 -0500112 self.assertIsNone(py27.timeout)
James E. Blaira7f51ca2017-02-07 16:01:26 -0800113 py27.inheritFrom(base)
114 self.assertEqual(30, py27.timeout)
115 self.assertEqual(['base-pre'],
116 [x.path for x in py27.pre_run])
117 self.assertEqual(['base-run'],
118 [x.path for x in py27.run])
119 self.assertEqual(['base-post'],
120 [x.path for x in py27.post_run])
Monty Taylor38b553a2017-06-05 13:06:10 -0500121 self.assertIsNone(py27.auth)
James E. Blaira7f51ca2017-02-07 16:01:26 -0800122
123 def test_job_variants(self):
124 # This simulates freezing a job.
125
James E. Blair74a82cf2017-07-12 17:23:08 -0700126 py27_pre = model.PlaybookContext(self.context, 'py27-pre', [])
127 py27_run = model.PlaybookContext(self.context, 'py27-run', [])
128 py27_post = model.PlaybookContext(self.context, 'py27-post', [])
James E. Blaira7f51ca2017-02-07 16:01:26 -0800129
130 py27 = model.Job('py27')
131 py27.timeout = 30
132 py27.pre_run = [py27_pre]
133 py27.run = [py27_run]
134 py27.post_run = [py27_post]
James E. Blair8525e2b2017-03-15 14:05:47 -0700135 auth = model.AuthContext()
136 auth.secrets.append('foo')
James E. Blaira7f51ca2017-02-07 16:01:26 -0800137 py27.auth = auth
138
139 job = py27.copy()
140 self.assertEqual(30, job.timeout)
141
142 # Apply the diablo variant
143 diablo = model.Job('py27')
144 diablo.timeout = 40
145 job.applyVariant(diablo)
146
147 self.assertEqual(40, job.timeout)
148 self.assertEqual(['py27-pre'],
149 [x.path for x in job.pre_run])
150 self.assertEqual(['py27-run'],
151 [x.path for x in job.run])
152 self.assertEqual(['py27-post'],
153 [x.path for x in job.post_run])
154 self.assertEqual(auth, job.auth)
155
156 # Set the job to final for the following checks
157 job.final = True
158 self.assertTrue(job.voting)
159
160 good_final = model.Job('py27')
161 good_final.voting = False
162 job.applyVariant(good_final)
163 self.assertFalse(job.voting)
164
165 bad_final = model.Job('py27')
166 bad_final.timeout = 600
167 with testtools.ExpectedException(
168 Exception,
169 "Unable to modify final job"):
170 job.applyVariant(bad_final)
171
172 def test_job_inheritance_configloader(self):
173 # TODO(jeblair): move this to a configloader test
James E. Blair5ac93842017-01-20 06:47:34 -0800174 tenant = model.Tenant('tenant')
James E. Blair6459db12017-06-29 14:57:20 -0700175 layout = model.Layout(tenant)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700176
177 pipeline = model.Pipeline('gate', layout)
178 layout.addPipeline(pipeline)
179 queue = model.ChangeQueue(pipeline)
James E. Blair0a899752017-03-29 13:22:16 -0700180 project = model.Project('project', self.source)
James E. Blair08d9b782017-06-29 14:22:48 -0700181 tpc = model.TenantProjectConfig(project)
182 tenant.addUntrustedProject(tpc)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700183
James E. Blair5ac93842017-01-20 06:47:34 -0800184 base = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800185 '_source_context': self.context,
186 '_start_mark': self.start_mark,
James E. Blair83005782015-12-11 14:46:03 -0800187 'name': 'base',
James E. Blair2bab6e72017-08-07 09:52:45 -0700188 'parent': None,
James E. Blair83005782015-12-11 14:46:03 -0800189 'timeout': 30,
James E. Blaira7f51ca2017-02-07 16:01:26 -0800190 'pre-run': 'base-pre',
191 'post-run': 'base-post',
James E. Blair1774dd52017-02-03 10:52:32 -0800192 'nodes': [{
193 'name': 'controller',
James E. Blair16d96a02017-06-08 11:32:56 -0700194 'label': 'base',
James E. Blair1774dd52017-02-03 10:52:32 -0800195 }],
James E. Blair83005782015-12-11 14:46:03 -0800196 })
197 layout.addJob(base)
James E. Blair5ac93842017-01-20 06:47:34 -0800198 python27 = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800199 '_source_context': self.context,
200 '_start_mark': self.start_mark,
James E. Blair83005782015-12-11 14:46:03 -0800201 'name': 'python27',
202 'parent': 'base',
James E. Blaira7f51ca2017-02-07 16:01:26 -0800203 'pre-run': 'py27-pre',
204 'post-run': 'py27-post',
James E. Blair1774dd52017-02-03 10:52:32 -0800205 'nodes': [{
206 'name': 'controller',
James E. Blair16d96a02017-06-08 11:32:56 -0700207 'label': 'new',
James E. Blair1774dd52017-02-03 10:52:32 -0800208 }],
James E. Blair83005782015-12-11 14:46:03 -0800209 'timeout': 40,
210 })
211 layout.addJob(python27)
James E. Blair5ac93842017-01-20 06:47:34 -0800212 python27diablo = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800213 '_source_context': self.context,
214 '_start_mark': self.start_mark,
James E. Blair83005782015-12-11 14:46:03 -0800215 'name': 'python27',
216 'branches': [
217 'stable/diablo'
218 ],
James E. Blaira7f51ca2017-02-07 16:01:26 -0800219 'pre-run': 'py27-diablo-pre',
220 'run': 'py27-diablo',
221 'post-run': 'py27-diablo-post',
James E. Blair1774dd52017-02-03 10:52:32 -0800222 'nodes': [{
223 'name': 'controller',
James E. Blair16d96a02017-06-08 11:32:56 -0700224 'label': 'old',
James E. Blair1774dd52017-02-03 10:52:32 -0800225 }],
James E. Blair83005782015-12-11 14:46:03 -0800226 'timeout': 50,
227 })
228 layout.addJob(python27diablo)
229
James E. Blair5ac93842017-01-20 06:47:34 -0800230 python27essex = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800231 '_source_context': self.context,
232 '_start_mark': self.start_mark,
James E. Blaira7f51ca2017-02-07 16:01:26 -0800233 'name': 'python27',
234 'branches': [
235 'stable/essex'
236 ],
237 'pre-run': 'py27-essex-pre',
238 'post-run': 'py27-essex-post',
239 })
240 layout.addJob(python27essex)
241
James E. Blairff555742017-02-19 11:34:27 -0800242 project_config = configloader.ProjectParser.fromYaml(tenant, layout, [{
James E. Blairec7ff302017-03-04 07:31:32 -0800243 '_source_context': self.context,
244 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700245 'name': 'project',
246 'gate': {
247 'jobs': [
248 'python27'
249 ]
250 }
James E. Blairff555742017-02-19 11:34:27 -0800251 }])
James E. Blairf59f3cf2017-02-19 14:50:26 -0800252 layout.addProjectConfig(project_config)
James E. Blair83005782015-12-11 14:46:03 -0800253
James E. Blair83005782015-12-11 14:46:03 -0800254 change = model.Change(project)
James E. Blair1774dd52017-02-03 10:52:32 -0800255 # Test master
James E. Blair83005782015-12-11 14:46:03 -0800256 change.branch = 'master'
257 item = queue.enqueueChange(change)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700258 item.current_build_set.layout = layout
James E. Blair83005782015-12-11 14:46:03 -0800259
260 self.assertTrue(base.changeMatches(change))
261 self.assertTrue(python27.changeMatches(change))
262 self.assertFalse(python27diablo.changeMatches(change))
James E. Blaira7f51ca2017-02-07 16:01:26 -0800263 self.assertFalse(python27essex.changeMatches(change))
James E. Blair83005782015-12-11 14:46:03 -0800264
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200265 item.freezeJobGraph()
James E. Blair83005782015-12-11 14:46:03 -0800266 self.assertEqual(len(item.getJobs()), 1)
267 job = item.getJobs()[0]
268 self.assertEqual(job.name, 'python27')
269 self.assertEqual(job.timeout, 40)
James E. Blair1774dd52017-02-03 10:52:32 -0800270 nodes = job.nodeset.getNodes()
271 self.assertEqual(len(nodes), 1)
James E. Blair16d96a02017-06-08 11:32:56 -0700272 self.assertEqual(nodes[0].label, 'new')
James E. Blaira7f51ca2017-02-07 16:01:26 -0800273 self.assertEqual([x.path for x in job.pre_run],
Tobias Henkel165450e2017-06-26 22:53:45 +0200274 ['base-pre',
275 'py27-pre'])
James E. Blaira7f51ca2017-02-07 16:01:26 -0800276 self.assertEqual([x.path for x in job.post_run],
Tobias Henkel165450e2017-06-26 22:53:45 +0200277 ['py27-post',
278 'base-post'])
James E. Blaira7f51ca2017-02-07 16:01:26 -0800279 self.assertEqual([x.path for x in job.run],
280 ['playbooks/python27',
281 'playbooks/base'])
James E. Blair83005782015-12-11 14:46:03 -0800282
James E. Blair1774dd52017-02-03 10:52:32 -0800283 # Test diablo
James E. Blair83005782015-12-11 14:46:03 -0800284 change.branch = 'stable/diablo'
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700285 item = queue.enqueueChange(change)
286 item.current_build_set.layout = layout
James E. Blair83005782015-12-11 14:46:03 -0800287
288 self.assertTrue(base.changeMatches(change))
289 self.assertTrue(python27.changeMatches(change))
290 self.assertTrue(python27diablo.changeMatches(change))
James E. Blaira7f51ca2017-02-07 16:01:26 -0800291 self.assertFalse(python27essex.changeMatches(change))
James E. Blair83005782015-12-11 14:46:03 -0800292
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200293 item.freezeJobGraph()
James E. Blair83005782015-12-11 14:46:03 -0800294 self.assertEqual(len(item.getJobs()), 1)
295 job = item.getJobs()[0]
296 self.assertEqual(job.name, 'python27')
297 self.assertEqual(job.timeout, 50)
James E. Blair1774dd52017-02-03 10:52:32 -0800298 nodes = job.nodeset.getNodes()
299 self.assertEqual(len(nodes), 1)
James E. Blair16d96a02017-06-08 11:32:56 -0700300 self.assertEqual(nodes[0].label, 'old')
James E. Blaira7f51ca2017-02-07 16:01:26 -0800301 self.assertEqual([x.path for x in job.pre_run],
Tobias Henkel165450e2017-06-26 22:53:45 +0200302 ['base-pre',
303 'py27-pre',
304 'py27-diablo-pre'])
James E. Blaira7f51ca2017-02-07 16:01:26 -0800305 self.assertEqual([x.path for x in job.post_run],
Tobias Henkel165450e2017-06-26 22:53:45 +0200306 ['py27-diablo-post',
307 'py27-post',
308 'base-post'])
James E. Blaira7f51ca2017-02-07 16:01:26 -0800309 self.assertEqual([x.path for x in job.run],
Tobias Henkel165450e2017-06-26 22:53:45 +0200310 ['py27-diablo']),
James E. Blaira7f51ca2017-02-07 16:01:26 -0800311
312 # Test essex
313 change.branch = 'stable/essex'
314 item = queue.enqueueChange(change)
315 item.current_build_set.layout = layout
316
317 self.assertTrue(base.changeMatches(change))
318 self.assertTrue(python27.changeMatches(change))
319 self.assertFalse(python27diablo.changeMatches(change))
320 self.assertTrue(python27essex.changeMatches(change))
321
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200322 item.freezeJobGraph()
James E. Blaira7f51ca2017-02-07 16:01:26 -0800323 self.assertEqual(len(item.getJobs()), 1)
324 job = item.getJobs()[0]
325 self.assertEqual(job.name, 'python27')
326 self.assertEqual([x.path for x in job.pre_run],
Tobias Henkel165450e2017-06-26 22:53:45 +0200327 ['base-pre',
328 'py27-pre',
329 'py27-essex-pre'])
James E. Blaira7f51ca2017-02-07 16:01:26 -0800330 self.assertEqual([x.path for x in job.post_run],
Tobias Henkel165450e2017-06-26 22:53:45 +0200331 ['py27-essex-post',
332 'py27-post',
333 'base-post'])
James E. Blaira7f51ca2017-02-07 16:01:26 -0800334 self.assertEqual([x.path for x in job.run],
335 ['playbooks/python27',
336 'playbooks/base'])
James E. Blairce8a2132016-05-19 15:21:52 -0700337
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000338 def test_job_auth_inheritance(self):
James E. Blair6459db12017-06-29 14:57:20 -0700339 tenant = self.tenant
340 layout = self.layout
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000341
James E. Blair01f83b72017-03-15 13:03:40 -0700342 conf = yaml.safe_load('''
343- secret:
344 name: pypi-credentials
345 data:
346 username: test-username
James E. Blair9118c012017-08-03 11:19:16 -0700347 longpassword: !encrypted/pkcs1-oaep
348 - BFhtdnm8uXx7kn79RFL/zJywmzLkT1GY78P3bOtp4WghUFWobkifSu7ZpaV4NeO0s71Y
349 Usi1wGZZL0LveZjUN0t6OU1VZKSG8R5Ly7urjaSo1pPVIq5Rtt/H7W14Lecd+cUeKb4j
350 oeusC9drN3AA8a4oykcVpt1wVqUnTbMGC9ARMCQP6eopcs1l7tzMseprW4RDNhIuz3CR
351 gd0QBMPl6VDoFgBPB8vxtJw+3m0rqBYZCLZgCXekqlny8s2s92nJMuUABbJOEcDRarzi
352 bDsSXsfJt1y+5n7yOURsC7lovMg4GF/vCl/0YMKjBO5bpv9EM5fToeKYyPGSKQoHOnCY
353 ceb3cAVcv5UawcCic8XjhEhp4K7WPdYf2HVAC/qtxhbpjTxG4U5Q/SoppOJ60WqEkQvb
354 Xs6n5Dvy7xmph6GWmU/bAv3eUK3pdD3xa2Ue1lHWz3U+rsYraI+AKYsMYx3RBlfAmCeC
355 1ve2BXPrqnOo7G8tnUvfdYPbK4Aakk0ds/AVqFHEZN+S6hRBmBjLaRFWZ3QSO1NjbBxW
356 naHKZYT7nkrJm8AMCgZU0ZArFLpaufKCeiK5ECSsDxic4FIsY1OkWT42qEUfL0Wd+150
357 AKGNZpPJnnP3QYY4W/MWcKH/zdO400+zWN52WevbSqZy90tqKDJrBkMl1ydqbuw1E4ZH
358 vIs=
359 - BFhtdnm8uXx7kn79RFL/zJywmzLkT1GY78P3bOtp4WghUFWobkifSu7ZpaV4NeO0s71Y
360 Usi1wGZZL0LveZjUN0t6OU1VZKSG8R5Ly7urjaSo1pPVIq5Rtt/H7W14Lecd+cUeKb4j
361 oeusC9drN3AA8a4oykcVpt1wVqUnTbMGC9ARMCQP6eopcs1l7tzMseprW4RDNhIuz3CR
362 gd0QBMPl6VDoFgBPB8vxtJw+3m0rqBYZCLZgCXekqlny8s2s92nJMuUABbJOEcDRarzi
363 bDsSXsfJt1y+5n7yOURsC7lovMg4GF/vCl/0YMKjBO5bpv9EM5fToeKYyPGSKQoHOnCY
364 ceb3cAVcv5UawcCic8XjhEhp4K7WPdYf2HVAC/qtxhbpjTxG4U5Q/SoppOJ60WqEkQvb
365 Xs6n5Dvy7xmph6GWmU/bAv3eUK3pdD3xa2Ue1lHWz3U+rsYraI+AKYsMYx3RBlfAmCeC
366 1ve2BXPrqnOo7G8tnUvfdYPbK4Aakk0ds/AVqFHEZN+S6hRBmBjLaRFWZ3QSO1NjbBxW
367 naHKZYT7nkrJm8AMCgZU0ZArFLpaufKCeiK5ECSsDxic4FIsY1OkWT42qEUfL0Wd+150
368 AKGNZpPJnnP3QYY4W/MWcKH/zdO400+zWN52WevbSqZy90tqKDJrBkMl1ydqbuw1E4ZH
369 vIs=
James E. Blair717e8e92017-03-17 11:03:27 -0700370 password: !encrypted/pkcs1-oaep |
James E. Blair9118c012017-08-03 11:19:16 -0700371 BFhtdnm8uXx7kn79RFL/zJywmzLkT1GY78P3bOtp4WghUFWobkifSu7ZpaV4NeO0s71Y
372 Usi1wGZZL0LveZjUN0t6OU1VZKSG8R5Ly7urjaSo1pPVIq5Rtt/H7W14Lecd+cUeKb4j
373 oeusC9drN3AA8a4oykcVpt1wVqUnTbMGC9ARMCQP6eopcs1l7tzMseprW4RDNhIuz3CR
374 gd0QBMPl6VDoFgBPB8vxtJw+3m0rqBYZCLZgCXekqlny8s2s92nJMuUABbJOEcDRarzi
375 bDsSXsfJt1y+5n7yOURsC7lovMg4GF/vCl/0YMKjBO5bpv9EM5fToeKYyPGSKQoHOnCY
376 ceb3cAVcv5UawcCic8XjhEhp4K7WPdYf2HVAC/qtxhbpjTxG4U5Q/SoppOJ60WqEkQvb
377 Xs6n5Dvy7xmph6GWmU/bAv3eUK3pdD3xa2Ue1lHWz3U+rsYraI+AKYsMYx3RBlfAmCeC
378 1ve2BXPrqnOo7G8tnUvfdYPbK4Aakk0ds/AVqFHEZN+S6hRBmBjLaRFWZ3QSO1NjbBxW
379 naHKZYT7nkrJm8AMCgZU0ZArFLpaufKCeiK5ECSsDxic4FIsY1OkWT42qEUfL0Wd+150
380 AKGNZpPJnnP3QYY4W/MWcKH/zdO400+zWN52WevbSqZy90tqKDJrBkMl1ydqbuw1E4ZH
381 vIs=
James E. Blair01f83b72017-03-15 13:03:40 -0700382''')[0]['secret']
383
384 conf['_source_context'] = self.context
385 conf['_start_mark'] = self.start_mark
386
387 secret = configloader.SecretParser.fromYaml(layout, conf)
388 layout.addSecret(secret)
389
James E. Blair6459db12017-06-29 14:57:20 -0700390 base = configloader.JobParser.fromYaml(self.tenant, self.layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800391 '_source_context': self.context,
392 '_start_mark': self.start_mark,
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000393 'name': 'base',
James E. Blair2bab6e72017-08-07 09:52:45 -0700394 'parent': None,
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000395 'timeout': 30,
396 })
397 layout.addJob(base)
James E. Blair5ac93842017-01-20 06:47:34 -0800398 pypi_upload_without_inherit = configloader.JobParser.fromYaml(
399 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800400 '_source_context': self.context,
401 '_start_mark': self.start_mark,
James E. Blair5ac93842017-01-20 06:47:34 -0800402 'name': 'pypi-upload-without-inherit',
403 'parent': 'base',
404 'timeout': 40,
405 'auth': {
406 'secrets': [
407 'pypi-credentials',
408 ]
409 }
410 })
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000411 layout.addJob(pypi_upload_without_inherit)
James E. Blair5ac93842017-01-20 06:47:34 -0800412 pypi_upload_with_inherit = configloader.JobParser.fromYaml(
413 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800414 '_source_context': self.context,
415 '_start_mark': self.start_mark,
James E. Blair5ac93842017-01-20 06:47:34 -0800416 'name': 'pypi-upload-with-inherit',
417 'parent': 'base',
418 'timeout': 40,
419 'auth': {
420 'inherit': True,
421 'secrets': [
422 'pypi-credentials',
423 ]
424 }
425 })
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000426 layout.addJob(pypi_upload_with_inherit)
427 pypi_upload_with_inherit_false = configloader.JobParser.fromYaml(
James E. Blair5ac93842017-01-20 06:47:34 -0800428 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800429 '_source_context': self.context,
430 '_start_mark': self.start_mark,
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000431 'name': 'pypi-upload-with-inherit-false',
432 'parent': 'base',
433 'timeout': 40,
434 'auth': {
435 'inherit': False,
Ricardo Carrillo Cruz12c892b2016-11-18 15:35:49 +0000436 'secrets': [
437 'pypi-credentials',
438 ]
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000439 }
440 })
441 layout.addJob(pypi_upload_with_inherit_false)
James E. Blair5ac93842017-01-20 06:47:34 -0800442 in_repo_job_without_inherit = configloader.JobParser.fromYaml(
443 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800444 '_source_context': self.context,
445 '_start_mark': self.start_mark,
James E. Blair5ac93842017-01-20 06:47:34 -0800446 'name': 'in-repo-job-without-inherit',
447 'parent': 'pypi-upload-without-inherit',
448 })
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000449 layout.addJob(in_repo_job_without_inherit)
James E. Blair5ac93842017-01-20 06:47:34 -0800450 in_repo_job_with_inherit = configloader.JobParser.fromYaml(
451 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800452 '_source_context': self.context,
453 '_start_mark': self.start_mark,
James E. Blair5ac93842017-01-20 06:47:34 -0800454 'name': 'in-repo-job-with-inherit',
455 'parent': 'pypi-upload-with-inherit',
456 })
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000457 layout.addJob(in_repo_job_with_inherit)
458 in_repo_job_with_inherit_false = configloader.JobParser.fromYaml(
James E. Blair5ac93842017-01-20 06:47:34 -0800459 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800460 '_source_context': self.context,
461 '_start_mark': self.start_mark,
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000462 'name': 'in-repo-job-with-inherit-false',
463 'parent': 'pypi-upload-with-inherit-false',
464 })
465 layout.addJob(in_repo_job_with_inherit_false)
466
Monty Taylor38b553a2017-06-05 13:06:10 -0500467 self.assertIsNone(in_repo_job_without_inherit.auth)
James E. Blair8525e2b2017-03-15 14:05:47 -0700468 self.assertEqual(1, len(in_repo_job_with_inherit.auth.secrets))
469 self.assertEqual(in_repo_job_with_inherit.auth.secrets[0].name,
470 'pypi-credentials')
Monty Taylor38b553a2017-06-05 13:06:10 -0500471 self.assertIsNone(in_repo_job_with_inherit_false.auth)
James E. Blair9118c012017-08-03 11:19:16 -0700472 self.assertEqual(in_repo_job_with_inherit.auth.secrets[0].
473 secret_data['longpassword'],
474 'test-passwordtest-password')
475 self.assertEqual(in_repo_job_with_inherit.auth.secrets[0].
476 secret_data['password'],
477 'test-password')
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000478
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700479 def test_job_inheritance_job_tree(self):
James E. Blair5ac93842017-01-20 06:47:34 -0800480 tenant = model.Tenant('tenant')
James E. Blair6459db12017-06-29 14:57:20 -0700481 layout = model.Layout(tenant)
James E. Blair08d9b782017-06-29 14:22:48 -0700482 tpc = model.TenantProjectConfig(self.project)
483 tenant.addUntrustedProject(tpc)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700484
485 pipeline = model.Pipeline('gate', layout)
486 layout.addPipeline(pipeline)
487 queue = model.ChangeQueue(pipeline)
488
James E. Blair5ac93842017-01-20 06:47:34 -0800489 base = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800490 '_source_context': self.context,
491 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700492 'name': 'base',
James E. Blair2bab6e72017-08-07 09:52:45 -0700493 'parent': None,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700494 'timeout': 30,
495 })
496 layout.addJob(base)
James E. Blair5ac93842017-01-20 06:47:34 -0800497 python27 = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800498 '_source_context': self.context,
499 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700500 'name': 'python27',
501 'parent': 'base',
502 'timeout': 40,
503 })
504 layout.addJob(python27)
James E. Blair5ac93842017-01-20 06:47:34 -0800505 python27diablo = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800506 '_source_context': self.context,
507 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700508 'name': 'python27',
509 'branches': [
510 'stable/diablo'
511 ],
512 'timeout': 50,
513 })
514 layout.addJob(python27diablo)
515
James E. Blairff555742017-02-19 11:34:27 -0800516 project_config = configloader.ProjectParser.fromYaml(tenant, layout, [{
James E. Blairec7ff302017-03-04 07:31:32 -0800517 '_source_context': self.context,
518 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700519 'name': 'project',
520 'gate': {
521 'jobs': [
522 {'python27': {'timeout': 70}}
523 ]
524 }
James E. Blairff555742017-02-19 11:34:27 -0800525 }])
James E. Blairf59f3cf2017-02-19 14:50:26 -0800526 layout.addProjectConfig(project_config)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700527
James E. Blairec7ff302017-03-04 07:31:32 -0800528 change = model.Change(self.project)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700529 change.branch = 'master'
530 item = queue.enqueueChange(change)
531 item.current_build_set.layout = layout
532
533 self.assertTrue(base.changeMatches(change))
534 self.assertTrue(python27.changeMatches(change))
535 self.assertFalse(python27diablo.changeMatches(change))
536
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200537 item.freezeJobGraph()
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700538 self.assertEqual(len(item.getJobs()), 1)
539 job = item.getJobs()[0]
540 self.assertEqual(job.name, 'python27')
541 self.assertEqual(job.timeout, 70)
542
543 change.branch = 'stable/diablo'
544 item = queue.enqueueChange(change)
545 item.current_build_set.layout = layout
546
547 self.assertTrue(base.changeMatches(change))
548 self.assertTrue(python27.changeMatches(change))
549 self.assertTrue(python27diablo.changeMatches(change))
550
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200551 item.freezeJobGraph()
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700552 self.assertEqual(len(item.getJobs()), 1)
553 job = item.getJobs()[0]
554 self.assertEqual(job.name, 'python27')
555 self.assertEqual(job.timeout, 70)
556
Clint Byrum85493602016-11-18 11:59:47 -0800557 def test_inheritance_keeps_matchers(self):
James E. Blair5ac93842017-01-20 06:47:34 -0800558 tenant = model.Tenant('tenant')
James E. Blair6459db12017-06-29 14:57:20 -0700559 layout = model.Layout(tenant)
Clint Byrum85493602016-11-18 11:59:47 -0800560
561 pipeline = model.Pipeline('gate', layout)
562 layout.addPipeline(pipeline)
563 queue = model.ChangeQueue(pipeline)
James E. Blair0a899752017-03-29 13:22:16 -0700564 project = model.Project('project', self.source)
James E. Blair08d9b782017-06-29 14:22:48 -0700565 tpc = model.TenantProjectConfig(project)
566 tenant.addUntrustedProject(tpc)
Clint Byrum85493602016-11-18 11:59:47 -0800567
James E. Blair5ac93842017-01-20 06:47:34 -0800568 base = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800569 '_source_context': self.context,
570 '_start_mark': self.start_mark,
Clint Byrum85493602016-11-18 11:59:47 -0800571 'name': 'base',
James E. Blair2bab6e72017-08-07 09:52:45 -0700572 'parent': None,
Clint Byrum85493602016-11-18 11:59:47 -0800573 'timeout': 30,
574 })
575 layout.addJob(base)
James E. Blair5ac93842017-01-20 06:47:34 -0800576 python27 = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800577 '_source_context': self.context,
578 '_start_mark': self.start_mark,
Clint Byrum85493602016-11-18 11:59:47 -0800579 'name': 'python27',
580 'parent': 'base',
581 'timeout': 40,
582 'irrelevant-files': ['^ignored-file$'],
583 })
584 layout.addJob(python27)
585
James E. Blairff555742017-02-19 11:34:27 -0800586 project_config = configloader.ProjectParser.fromYaml(tenant, layout, [{
James E. Blairec7ff302017-03-04 07:31:32 -0800587 '_source_context': self.context,
588 '_start_mark': self.start_mark,
Clint Byrum85493602016-11-18 11:59:47 -0800589 'name': 'project',
590 'gate': {
591 'jobs': [
592 'python27',
593 ]
594 }
James E. Blairff555742017-02-19 11:34:27 -0800595 }])
James E. Blairf59f3cf2017-02-19 14:50:26 -0800596 layout.addProjectConfig(project_config)
Clint Byrum85493602016-11-18 11:59:47 -0800597
598 change = model.Change(project)
599 change.branch = 'master'
600 change.files = ['/COMMIT_MSG', 'ignored-file']
601 item = queue.enqueueChange(change)
602 item.current_build_set.layout = layout
603
604 self.assertTrue(base.changeMatches(change))
605 self.assertFalse(python27.changeMatches(change))
606
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200607 item.freezeJobGraph()
Clint Byrum85493602016-11-18 11:59:47 -0800608 self.assertEqual([], item.getJobs())
609
James E. Blair4317e9f2016-07-15 10:05:47 -0700610 def test_job_source_project(self):
James E. Blair6459db12017-06-29 14:57:20 -0700611 tenant = self.tenant
612 layout = self.layout
James E. Blair0a899752017-03-29 13:22:16 -0700613 base_project = model.Project('base_project', self.source)
James E. Blair6f140c72017-03-03 10:32:07 -0800614 base_context = model.SourceContext(base_project, 'master',
615 'test', True)
James E. Blair6459db12017-06-29 14:57:20 -0700616 tpc = model.TenantProjectConfig(base_project)
617 tenant.addUntrustedProject(tpc)
James E. Blaircdab2032017-02-01 09:09:29 -0800618
James E. Blair5ac93842017-01-20 06:47:34 -0800619 base = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blaircdab2032017-02-01 09:09:29 -0800620 '_source_context': base_context,
James E. Blairec7ff302017-03-04 07:31:32 -0800621 '_start_mark': self.start_mark,
James E. Blair2bab6e72017-08-07 09:52:45 -0700622 'parent': None,
James E. Blair4317e9f2016-07-15 10:05:47 -0700623 'name': 'base',
624 })
625 layout.addJob(base)
626
James E. Blair0a899752017-03-29 13:22:16 -0700627 other_project = model.Project('other_project', self.source)
James E. Blair6f140c72017-03-03 10:32:07 -0800628 other_context = model.SourceContext(other_project, 'master',
629 'test', True)
James E. Blair6459db12017-06-29 14:57:20 -0700630 tpc = model.TenantProjectConfig(other_project)
631 tenant.addUntrustedProject(tpc)
James E. Blair5ac93842017-01-20 06:47:34 -0800632 base2 = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blaircdab2032017-02-01 09:09:29 -0800633 '_source_context': other_context,
James E. Blairec7ff302017-03-04 07:31:32 -0800634 '_start_mark': self.start_mark,
James E. Blair4317e9f2016-07-15 10:05:47 -0700635 'name': 'base',
636 })
637 with testtools.ExpectedException(
638 Exception,
639 "Job base in other_project is not permitted "
640 "to shadow job base in base_project"):
641 layout.addJob(base2)
642
James E. Blairb3f5db12017-03-17 12:57:39 -0700643 def test_job_allowed_projects(self):
644 job = configloader.JobParser.fromYaml(self.tenant, self.layout, {
645 '_source_context': self.context,
646 '_start_mark': self.start_mark,
647 'name': 'job',
James E. Blair2bab6e72017-08-07 09:52:45 -0700648 'parent': None,
James E. Blairb3f5db12017-03-17 12:57:39 -0700649 'allowed-projects': ['project'],
650 })
651 self.layout.addJob(job)
652
James E. Blair0a899752017-03-29 13:22:16 -0700653 project2 = model.Project('project2', self.source)
James E. Blair08d9b782017-06-29 14:22:48 -0700654 tpc2 = model.TenantProjectConfig(project2)
655 self.tenant.addUntrustedProject(tpc2)
James E. Blairb3f5db12017-03-17 12:57:39 -0700656 context2 = model.SourceContext(project2, 'master',
657 'test', True)
658
659 project2_config = configloader.ProjectParser.fromYaml(
660 self.tenant, self.layout, [{
661 '_source_context': context2,
662 '_start_mark': self.start_mark,
663 'name': 'project2',
664 'gate': {
665 'jobs': [
666 'job'
667 ]
668 }
669 }]
670 )
671 self.layout.addProjectConfig(project2_config)
672
673 change = model.Change(project2)
674 # Test master
675 change.branch = 'master'
676 item = self.queue.enqueueChange(change)
677 item.current_build_set.layout = self.layout
678 with testtools.ExpectedException(
679 Exception,
680 "Project project2 is not allowed to run job job"):
681 item.freezeJobGraph()
682
James E. Blaird2348362017-03-17 13:59:35 -0700683 def test_job_pipeline_allow_secrets(self):
684 self.pipeline.allow_secrets = False
685 job = configloader.JobParser.fromYaml(self.tenant, self.layout, {
686 '_source_context': self.context,
687 '_start_mark': self.start_mark,
688 'name': 'job',
James E. Blair2bab6e72017-08-07 09:52:45 -0700689 'parent': None,
James E. Blaird2348362017-03-17 13:59:35 -0700690 })
691 auth = model.AuthContext()
692 auth.secrets.append('foo')
693 job.auth = auth
694
695 self.layout.addJob(job)
696
697 project_config = configloader.ProjectParser.fromYaml(
698 self.tenant, self.layout, [{
699 '_source_context': self.context,
700 '_start_mark': self.start_mark,
701 'name': 'project',
702 'gate': {
703 'jobs': [
704 'job'
705 ]
706 }
707 }]
708 )
709 self.layout.addProjectConfig(project_config)
710
711 change = model.Change(self.project)
712 # Test master
713 change.branch = 'master'
714 item = self.queue.enqueueChange(change)
715 item.current_build_set.layout = self.layout
716 with testtools.ExpectedException(
717 Exception,
718 "Pipeline gate does not allow jobs with secrets"):
719 item.freezeJobGraph()
720
James E. Blairce8a2132016-05-19 15:21:52 -0700721
722class TestJobTimeData(BaseTestCase):
723 def setUp(self):
724 super(TestJobTimeData, self).setUp()
725 self.tmp_root = self.useFixture(fixtures.TempDir(
726 rootdir=os.environ.get("ZUUL_TEST_ROOT"))
727 ).path
728
729 def test_empty_timedata(self):
730 path = os.path.join(self.tmp_root, 'job-name')
731 self.assertFalse(os.path.exists(path))
732 self.assertFalse(os.path.exists(path + '.tmp'))
733 td = model.JobTimeData(path)
734 self.assertEqual(td.success_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
735 self.assertEqual(td.failure_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
736 self.assertEqual(td.results, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
737
738 def test_save_reload(self):
739 path = os.path.join(self.tmp_root, 'job-name')
740 self.assertFalse(os.path.exists(path))
741 self.assertFalse(os.path.exists(path + '.tmp'))
742 td = model.JobTimeData(path)
743 self.assertEqual(td.success_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
744 self.assertEqual(td.failure_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
745 self.assertEqual(td.results, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
746 success_times = []
747 failure_times = []
748 results = []
749 for x in range(10):
750 success_times.append(int(random.random() * 1000))
751 failure_times.append(int(random.random() * 1000))
752 results.append(0)
753 results.append(1)
754 random.shuffle(results)
755 s = f = 0
756 for result in results:
757 if result:
758 td.add(failure_times[f], 'FAILURE')
759 f += 1
760 else:
761 td.add(success_times[s], 'SUCCESS')
762 s += 1
763 self.assertEqual(td.success_times, success_times)
764 self.assertEqual(td.failure_times, failure_times)
765 self.assertEqual(td.results, results[10:])
766 td.save()
767 self.assertTrue(os.path.exists(path))
768 self.assertFalse(os.path.exists(path + '.tmp'))
769 td = model.JobTimeData(path)
770 td.load()
771 self.assertEqual(td.success_times, success_times)
772 self.assertEqual(td.failure_times, failure_times)
773 self.assertEqual(td.results, results[10:])
774
775
776class TestTimeDataBase(BaseTestCase):
777 def setUp(self):
778 super(TestTimeDataBase, self).setUp()
779 self.tmp_root = self.useFixture(fixtures.TempDir(
780 rootdir=os.environ.get("ZUUL_TEST_ROOT"))
781 ).path
782 self.db = model.TimeDataBase(self.tmp_root)
783
784 def test_timedatabase(self):
785 self.assertEqual(self.db.getEstimatedTime('job-name'), 0)
786 self.db.update('job-name', 50, 'SUCCESS')
787 self.assertEqual(self.db.getEstimatedTime('job-name'), 50)
788 self.db.update('job-name', 100, 'SUCCESS')
789 self.assertEqual(self.db.getEstimatedTime('job-name'), 75)
790 for x in range(10):
791 self.db.update('job-name', 100, 'SUCCESS')
792 self.assertEqual(self.db.getEstimatedTime('job-name'), 100)
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200793
794
795class TestGraph(BaseTestCase):
796 def test_job_graph_disallows_multiple_jobs_with_same_name(self):
797 graph = model.JobGraph()
798 job1 = model.Job('job')
799 job2 = model.Job('job')
800 graph.addJob(job1)
801 with testtools.ExpectedException(Exception,
802 "Job job already added"):
803 graph.addJob(job2)
804
805 def test_job_graph_disallows_circular_dependencies(self):
806 graph = model.JobGraph()
807 jobs = [model.Job('job%d' % i) for i in range(0, 10)]
808 prevjob = None
809 for j in jobs[:3]:
810 if prevjob:
811 j.dependencies = frozenset([prevjob.name])
812 graph.addJob(j)
813 prevjob = j
814 # 0 triggers 1 triggers 2 triggers 3...
815
816 # Cannot depend on itself
817 with testtools.ExpectedException(
818 Exception,
819 "Dependency cycle detected in job jobX"):
820 j = model.Job('jobX')
821 j.dependencies = frozenset([j.name])
822 graph.addJob(j)
823
824 # Disallow circular dependencies
825 with testtools.ExpectedException(
826 Exception,
827 "Dependency cycle detected in job job3"):
828 jobs[4].dependencies = frozenset([jobs[3].name])
829 graph.addJob(jobs[4])
830 jobs[3].dependencies = frozenset([jobs[4].name])
831 graph.addJob(jobs[3])
832
833 jobs[5].dependencies = frozenset([jobs[4].name])
834 graph.addJob(jobs[5])
835
836 with testtools.ExpectedException(
837 Exception,
838 "Dependency cycle detected in job job3"):
839 jobs[3].dependencies = frozenset([jobs[5].name])
840 graph.addJob(jobs[3])
841
842 jobs[3].dependencies = frozenset([jobs[2].name])
843 graph.addJob(jobs[3])
844 jobs[6].dependencies = frozenset([jobs[2].name])
845 graph.addJob(jobs[6])
James E. Blairc2a54fd2017-03-29 15:19:26 -0700846
847
848class TestTenant(BaseTestCase):
849 def test_add_project(self):
850 tenant = model.Tenant('tenant')
851 connection1 = Dummy(connection_name='dummy_connection1')
852 source1 = Dummy(canonical_hostname='git1.example.com',
853 name='dummy', # TODOv3(jeblair): remove
854 connection=connection1)
855
856 source1_project1 = model.Project('project1', source1)
James E. Blair08d9b782017-06-29 14:22:48 -0700857 source1_project1_tpc = model.TenantProjectConfig(source1_project1)
858 tenant.addConfigProject(source1_project1_tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700859 d = {'project1':
860 {'git1.example.com': source1_project1}}
861 self.assertEqual(d, tenant.projects)
862 self.assertEqual((True, source1_project1),
863 tenant.getProject('project1'))
864 self.assertEqual((True, source1_project1),
865 tenant.getProject('git1.example.com/project1'))
866
867 source1_project2 = model.Project('project2', source1)
James E. Blair08d9b782017-06-29 14:22:48 -0700868 tpc = model.TenantProjectConfig(source1_project2)
869 tenant.addUntrustedProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700870 d = {'project1':
871 {'git1.example.com': source1_project1},
872 'project2':
873 {'git1.example.com': source1_project2}}
874 self.assertEqual(d, tenant.projects)
875 self.assertEqual((False, source1_project2),
876 tenant.getProject('project2'))
877 self.assertEqual((False, source1_project2),
878 tenant.getProject('git1.example.com/project2'))
879
880 connection2 = Dummy(connection_name='dummy_connection2')
881 source2 = Dummy(canonical_hostname='git2.example.com',
882 name='dummy', # TODOv3(jeblair): remove
883 connection=connection2)
884
885 source2_project1 = model.Project('project1', source2)
James E. Blair08d9b782017-06-29 14:22:48 -0700886 tpc = model.TenantProjectConfig(source2_project1)
887 tenant.addUntrustedProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700888 d = {'project1':
889 {'git1.example.com': source1_project1,
890 'git2.example.com': source2_project1},
891 'project2':
892 {'git1.example.com': source1_project2}}
893 self.assertEqual(d, tenant.projects)
894 with testtools.ExpectedException(
895 Exception,
896 "Project name 'project1' is ambiguous"):
897 tenant.getProject('project1')
898 self.assertEqual((False, source1_project2),
899 tenant.getProject('project2'))
900 self.assertEqual((True, source1_project1),
901 tenant.getProject('git1.example.com/project1'))
902 self.assertEqual((False, source2_project1),
903 tenant.getProject('git2.example.com/project1'))
904
905 source2_project2 = model.Project('project2', source2)
James E. Blair08d9b782017-06-29 14:22:48 -0700906 tpc = model.TenantProjectConfig(source2_project2)
907 tenant.addConfigProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700908 d = {'project1':
909 {'git1.example.com': source1_project1,
910 'git2.example.com': source2_project1},
911 'project2':
912 {'git1.example.com': source1_project2,
913 'git2.example.com': source2_project2}}
914 self.assertEqual(d, tenant.projects)
915 with testtools.ExpectedException(
916 Exception,
917 "Project name 'project1' is ambiguous"):
918 tenant.getProject('project1')
919 with testtools.ExpectedException(
920 Exception,
921 "Project name 'project2' is ambiguous"):
922 tenant.getProject('project2')
923 self.assertEqual((True, source1_project1),
924 tenant.getProject('git1.example.com/project1'))
925 self.assertEqual((False, source2_project1),
926 tenant.getProject('git2.example.com/project1'))
927 self.assertEqual((False, source1_project2),
928 tenant.getProject('git1.example.com/project2'))
929 self.assertEqual((True, source2_project2),
930 tenant.getProject('git2.example.com/project2'))
931
932 source1_project2b = model.Project('subpath/project2', source1)
James E. Blair08d9b782017-06-29 14:22:48 -0700933 tpc = model.TenantProjectConfig(source1_project2b)
934 tenant.addConfigProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700935 d = {'project1':
936 {'git1.example.com': source1_project1,
937 'git2.example.com': source2_project1},
938 'project2':
939 {'git1.example.com': source1_project2,
940 'git2.example.com': source2_project2},
941 'subpath/project2':
942 {'git1.example.com': source1_project2b}}
943 self.assertEqual(d, tenant.projects)
944 self.assertEqual((False, source1_project2),
945 tenant.getProject('git1.example.com/project2'))
946 self.assertEqual((True, source2_project2),
947 tenant.getProject('git2.example.com/project2'))
948 self.assertEqual((True, source1_project2b),
949 tenant.getProject('subpath/project2'))
950 self.assertEqual(
951 (True, source1_project2b),
952 tenant.getProject('git1.example.com/subpath/project2'))
953
954 source2_project2b = model.Project('subpath/project2', source2)
James E. Blair08d9b782017-06-29 14:22:48 -0700955 tpc = model.TenantProjectConfig(source2_project2b)
956 tenant.addConfigProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700957 d = {'project1':
958 {'git1.example.com': source1_project1,
959 'git2.example.com': source2_project1},
960 'project2':
961 {'git1.example.com': source1_project2,
962 'git2.example.com': source2_project2},
963 'subpath/project2':
964 {'git1.example.com': source1_project2b,
965 'git2.example.com': source2_project2b}}
966 self.assertEqual(d, tenant.projects)
967 self.assertEqual((False, source1_project2),
968 tenant.getProject('git1.example.com/project2'))
969 self.assertEqual((True, source2_project2),
970 tenant.getProject('git2.example.com/project2'))
971 with testtools.ExpectedException(
972 Exception,
973 "Project name 'subpath/project2' is ambiguous"):
974 tenant.getProject('subpath/project2')
975 self.assertEqual(
976 (True, source1_project2b),
977 tenant.getProject('git1.example.com/subpath/project2'))
978 self.assertEqual(
979 (True, source2_project2b),
980 tenant.getProject('git2.example.com/subpath/project2'))
981
982 with testtools.ExpectedException(
983 Exception,
984 "Project project1 is already in project index"):
James E. Blair08d9b782017-06-29 14:22:48 -0700985 tenant._addProject(source1_project1_tpc)