The Gatekeeper, or a project gating system
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

776 lines
30 KiB

  1. # Copyright 2012 Hewlett-Packard Development Company, L.P.
  2. # Copyright 2018 Red Hat, Inc.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. # not use this file except in compliance with the License. You may obtain
  6. # a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. # License for the specific language governing permissions and limitations
  14. # under the License.
  15. from tests.base import (
  16. ZuulTestCase,
  17. simple_layout,
  18. )
  19. URL_FORMATS = [
  20. '{baseurl}/{change_no}',
  21. '{baseurl}/#/c/{change_no}',
  22. '{baseurl}/c/{project}/+/{change_no}/',
  23. '{change_id}',
  24. ]
  25. class TestGerritCRD(ZuulTestCase):
  26. tenant_config_file = 'config/single-tenant/main.yaml'
  27. def _test_crd_gate(self, url_fmt):
  28. "Test cross-repo dependencies"
  29. A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
  30. B = self.fake_gerrit.addFakeChange('org/project2', 'master', 'B')
  31. A.addApproval('Code-Review', 2)
  32. B.addApproval('Code-Review', 2)
  33. AM2 = self.fake_gerrit.addFakeChange('org/project1', 'master', 'AM2')
  34. AM1 = self.fake_gerrit.addFakeChange('org/project1', 'master', 'AM1')
  35. AM2.setMerged()
  36. AM1.setMerged()
  37. BM2 = self.fake_gerrit.addFakeChange('org/project2', 'master', 'BM2')
  38. BM1 = self.fake_gerrit.addFakeChange('org/project2', 'master', 'BM1')
  39. BM2.setMerged()
  40. BM1.setMerged()
  41. # A -> AM1 -> AM2
  42. # B -> BM1 -> BM2
  43. # A Depends-On: B
  44. # M2 is here to make sure it is never queried. If it is, it
  45. # means zuul is walking down the entire history of merged
  46. # changes.
  47. B.setDependsOn(BM1, 1)
  48. BM1.setDependsOn(BM2, 1)
  49. A.setDependsOn(AM1, 1)
  50. AM1.setDependsOn(AM2, 1)
  51. url = url_fmt.format(baseurl=B.gerrit.baseurl.rstrip('/'),
  52. project=B.project,
  53. change_no=B.number,
  54. change_id=B.data['id'])
  55. A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  56. A.subject, url)
  57. self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
  58. self.waitUntilSettled()
  59. self.assertEqual(A.data['status'], 'NEW')
  60. self.assertEqual(B.data['status'], 'NEW')
  61. for connection in self.scheds.first.connections.connections.values():
  62. connection.maintainCache([])
  63. self.executor_server.hold_jobs_in_build = True
  64. B.addApproval('Approved', 1)
  65. self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
  66. self.waitUntilSettled()
  67. self.executor_server.release('.*-merge')
  68. self.waitUntilSettled()
  69. self.executor_server.release('.*-merge')
  70. self.waitUntilSettled()
  71. self.executor_server.hold_jobs_in_build = False
  72. self.executor_server.release()
  73. self.waitUntilSettled()
  74. self.assertEqual(AM2.queried, 0)
  75. self.assertEqual(BM2.queried, 0)
  76. self.assertEqual(A.data['status'], 'MERGED')
  77. self.assertEqual(B.data['status'], 'MERGED')
  78. self.assertEqual(A.reported, 2)
  79. self.assertEqual(B.reported, 2)
  80. changes = self.getJobFromHistory(
  81. 'project-merge', 'org/project1').changes
  82. self.assertEqual(changes, '2,1 1,1')
  83. # Different versions of Gerrit have used 3 different URL schemata for
  84. # changes - repeat the simple test on each of the 3 to ensure they can be
  85. # parsed, the other tests just use the default URL schema provided in
  86. # FakeGerritChange.data['url'] .
  87. # This list also includes the legacy change id.
  88. def test_crd_gate_url_schema0(self):
  89. self._test_crd_gate(URL_FORMATS[0])
  90. def test_crd_gate_url_schema1(self):
  91. self._test_crd_gate(URL_FORMATS[1])
  92. def test_crd_gate_url_schema2(self):
  93. self._test_crd_gate(URL_FORMATS[2])
  94. def test_crd_gate_legacy_id(self):
  95. self._test_crd_gate(URL_FORMATS[3])
  96. def test_crd_gate_triangle(self):
  97. A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
  98. B = self.fake_gerrit.addFakeChange('org/project2', 'master', 'B')
  99. C = self.fake_gerrit.addFakeChange('org/project2', 'master', 'C')
  100. A.addApproval('Code-Review', 2)
  101. B.addApproval('Code-Review', 2)
  102. C.addApproval('Code-Review', 2)
  103. A.addApproval('Approved', 1)
  104. B.addApproval('Approved', 1)
  105. # C-->B
  106. # \ /
  107. # v
  108. # A
  109. # C Depends-On: A
  110. C.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  111. C.subject, A.data['url'])
  112. # B Depends-On: A
  113. B.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  114. B.subject, A.data['url'])
  115. # C git-depends on B
  116. C.setDependsOn(B, 1)
  117. self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
  118. self.waitUntilSettled()
  119. self.assertEqual(A.reported, 2)
  120. self.assertEqual(B.reported, 2)
  121. self.assertEqual(C.reported, 2)
  122. self.assertEqual(A.data['status'], 'MERGED')
  123. self.assertEqual(B.data['status'], 'MERGED')
  124. self.assertEqual(C.data['status'], 'MERGED')
  125. self.assertEqual(self.history[-1].changes, '1,1 2,1 3,1')
  126. def test_crd_branch(self):
  127. "Test cross-repo dependencies in multiple branches"
  128. self.create_branch('org/project2', 'mp')
  129. A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
  130. B = self.fake_gerrit.addFakeChange('org/project2', 'master', 'B')
  131. C1 = self.fake_gerrit.addFakeChange('org/project2', 'mp', 'C1')
  132. A.addApproval('Code-Review', 2)
  133. B.addApproval('Code-Review', 2)
  134. C1.addApproval('Code-Review', 2)
  135. # A Depends-On: B+C1
  136. A.data['commitMessage'] = '%s\n\nDepends-On: %s\nDepends-On: %s\n' % (
  137. A.subject, B.data['url'], C1.data['url'])
  138. self.executor_server.hold_jobs_in_build = True
  139. B.addApproval('Approved', 1)
  140. C1.addApproval('Approved', 1)
  141. self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
  142. self.waitUntilSettled()
  143. self.executor_server.release('.*-merge')
  144. self.waitUntilSettled()
  145. self.executor_server.release('.*-merge')
  146. self.waitUntilSettled()
  147. self.executor_server.release('.*-merge')
  148. self.waitUntilSettled()
  149. self.executor_server.hold_jobs_in_build = False
  150. self.executor_server.release()
  151. self.waitUntilSettled()
  152. self.assertEqual(A.data['status'], 'MERGED')
  153. self.assertEqual(B.data['status'], 'MERGED')
  154. self.assertEqual(C1.data['status'], 'MERGED')
  155. self.assertEqual(A.reported, 2)
  156. self.assertEqual(B.reported, 2)
  157. self.assertEqual(C1.reported, 2)
  158. changes = self.getJobFromHistory(
  159. 'project-merge', 'org/project1').changes
  160. self.assertEqual(changes, '2,1 3,1 1,1')
  161. def test_crd_multiline(self):
  162. "Test multiple depends-on lines in commit"
  163. A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
  164. B = self.fake_gerrit.addFakeChange('org/project2', 'master', 'B')
  165. C = self.fake_gerrit.addFakeChange('org/project2', 'master', 'C')
  166. A.addApproval('Code-Review', 2)
  167. B.addApproval('Code-Review', 2)
  168. C.addApproval('Code-Review', 2)
  169. # A Depends-On: B+C
  170. A.data['commitMessage'] = '%s\n\nDepends-On: %s\nDepends-On: %s\n' % (
  171. A.subject, B.data['url'], C.data['url'])
  172. self.executor_server.hold_jobs_in_build = True
  173. B.addApproval('Approved', 1)
  174. C.addApproval('Approved', 1)
  175. self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
  176. self.waitUntilSettled()
  177. self.executor_server.release('.*-merge')
  178. self.waitUntilSettled()
  179. self.executor_server.release('.*-merge')
  180. self.waitUntilSettled()
  181. self.executor_server.release('.*-merge')
  182. self.waitUntilSettled()
  183. self.executor_server.hold_jobs_in_build = False
  184. self.executor_server.release()
  185. self.waitUntilSettled()
  186. self.assertEqual(A.data['status'], 'MERGED')
  187. self.assertEqual(B.data['status'], 'MERGED')
  188. self.assertEqual(C.data['status'], 'MERGED')
  189. self.assertEqual(A.reported, 2)
  190. self.assertEqual(B.reported, 2)
  191. self.assertEqual(C.reported, 2)
  192. changes = self.getJobFromHistory(
  193. 'project-merge', 'org/project1').changes
  194. self.assertEqual(changes, '2,1 3,1 1,1')
  195. def test_crd_unshared_gate(self):
  196. "Test cross-repo dependencies in unshared gate queues"
  197. A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
  198. B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
  199. A.addApproval('Code-Review', 2)
  200. B.addApproval('Code-Review', 2)
  201. # A Depends-On: B
  202. A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  203. A.subject, B.data['url'])
  204. # A and B do not share a queue, make sure that A is unable to
  205. # enqueue B (and therefore, A is unable to be enqueued).
  206. B.addApproval('Approved', 1)
  207. self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
  208. self.waitUntilSettled()
  209. self.assertEqual(A.data['status'], 'NEW')
  210. self.assertEqual(B.data['status'], 'NEW')
  211. self.assertEqual(A.reported, 0)
  212. self.assertEqual(B.reported, 0)
  213. self.assertEqual(len(self.history), 0)
  214. # Enqueue and merge B alone.
  215. self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
  216. self.waitUntilSettled()
  217. self.assertEqual(B.data['status'], 'MERGED')
  218. self.assertEqual(B.reported, 2)
  219. # Now that B is merged, A should be able to be enqueued and
  220. # merged.
  221. self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
  222. self.waitUntilSettled()
  223. self.assertEqual(A.data['status'], 'MERGED')
  224. self.assertEqual(A.reported, 2)
  225. def _test_crd_gate_reverse(self, url_fmt):
  226. "Test reverse cross-repo dependencies"
  227. A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
  228. B = self.fake_gerrit.addFakeChange('org/project2', 'master', 'B')
  229. A.addApproval('Code-Review', 2)
  230. B.addApproval('Code-Review', 2)
  231. # A Depends-On: B
  232. url = url_fmt.format(baseurl=B.gerrit.baseurl.rstrip('/'),
  233. project=B.project,
  234. change_no=B.number)
  235. A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  236. A.subject, url)
  237. self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
  238. self.waitUntilSettled()
  239. self.assertEqual(A.data['status'], 'NEW')
  240. self.assertEqual(B.data['status'], 'NEW')
  241. self.executor_server.hold_jobs_in_build = True
  242. A.addApproval('Approved', 1)
  243. self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
  244. self.waitUntilSettled()
  245. self.executor_server.release('.*-merge')
  246. self.waitUntilSettled()
  247. self.executor_server.release('.*-merge')
  248. self.waitUntilSettled()
  249. self.executor_server.hold_jobs_in_build = False
  250. self.executor_server.release()
  251. self.waitUntilSettled()
  252. self.assertEqual(A.data['status'], 'MERGED')
  253. self.assertEqual(B.data['status'], 'MERGED')
  254. self.assertEqual(A.reported, 2)
  255. self.assertEqual(B.reported, 2)
  256. changes = self.getJobFromHistory(
  257. 'project-merge', 'org/project1').changes
  258. self.assertEqual(changes, '2,1 1,1')
  259. def test_crd_gate_reverse_schema0(self):
  260. self._test_crd_gate_reverse(URL_FORMATS[0])
  261. def test_crd_gate_reverse_schema1(self):
  262. self._test_crd_gate_reverse(URL_FORMATS[1])
  263. def test_crd_gate_reverse_schema2(self):
  264. self._test_crd_gate_reverse(URL_FORMATS[2])
  265. def test_crd_cycle(self):
  266. "Test cross-repo dependency cycles"
  267. A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
  268. B = self.fake_gerrit.addFakeChange('org/project2', 'master', 'B')
  269. A.addApproval('Code-Review', 2)
  270. B.addApproval('Code-Review', 2)
  271. B.addApproval('Approved', 1)
  272. # A -> B -> A (via commit-depends)
  273. A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  274. A.subject, B.data['url'])
  275. B.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  276. B.subject, A.data['url'])
  277. self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
  278. self.waitUntilSettled()
  279. self.assertEqual(A.reported, 1)
  280. self.assertEqual(B.reported, 0)
  281. self.assertEqual(A.data['status'], 'NEW')
  282. self.assertEqual(B.data['status'], 'NEW')
  283. def test_crd_gate_unknown(self):
  284. "Test unknown projects in dependent pipeline"
  285. self.init_repo("org/unknown", tag='init')
  286. A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
  287. B = self.fake_gerrit.addFakeChange('org/unknown', 'master', 'B')
  288. A.addApproval('Code-Review', 2)
  289. B.addApproval('Code-Review', 2)
  290. # A Depends-On: B
  291. A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  292. A.subject, B.data['url'])
  293. B.addApproval('Approved', 1)
  294. self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
  295. self.waitUntilSettled()
  296. # Unknown projects cannot share a queue with any other
  297. # since they don't have common jobs with any other (they have no jobs).
  298. # Changes which depend on unknown project changes
  299. # should not be processed in dependent pipeline
  300. self.assertEqual(A.data['status'], 'NEW')
  301. self.assertEqual(B.data['status'], 'NEW')
  302. self.assertEqual(A.reported, 0)
  303. self.assertEqual(B.reported, 0)
  304. self.assertEqual(len(self.history), 0)
  305. # Simulate change B being gated outside this layout Set the
  306. # change merged before submitting the event so that when the
  307. # event triggers a gerrit query to update the change, we get
  308. # the information that it was merged.
  309. B.setMerged()
  310. self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
  311. self.waitUntilSettled()
  312. self.assertEqual(len(self.history), 0)
  313. # Now that B is merged, A should be able to be enqueued and
  314. # merged.
  315. self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
  316. self.waitUntilSettled()
  317. self.assertEqual(A.data['status'], 'MERGED')
  318. self.assertEqual(A.reported, 2)
  319. self.assertEqual(B.data['status'], 'MERGED')
  320. self.assertEqual(B.reported, 0)
  321. def test_crd_check(self):
  322. "Test cross-repo dependencies in independent pipelines"
  323. self.executor_server.hold_jobs_in_build = True
  324. self.gearman_server.hold_jobs_in_queue = True
  325. A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
  326. B = self.fake_gerrit.addFakeChange('org/project2', 'master', 'B')
  327. # A Depends-On: B
  328. A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  329. A.subject, B.data['url'])
  330. self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
  331. self.waitUntilSettled()
  332. self.gearman_server.hold_jobs_in_queue = False
  333. self.gearman_server.release()
  334. self.waitUntilSettled()
  335. self.executor_server.release('.*-merge')
  336. self.waitUntilSettled()
  337. self.assertTrue(self.builds[0].hasChanges(A, B))
  338. self.executor_server.hold_jobs_in_build = False
  339. self.executor_server.release()
  340. self.waitUntilSettled()
  341. self.assertEqual(A.data['status'], 'NEW')
  342. self.assertEqual(B.data['status'], 'NEW')
  343. self.assertEqual(A.reported, 1)
  344. self.assertEqual(B.reported, 0)
  345. self.assertEqual(self.history[0].changes, '2,1 1,1')
  346. tenant = self.scheds.first.sched.abide.tenants.get('tenant-one')
  347. self.assertEqual(len(tenant.layout.pipelines['check'].queues), 0)
  348. def test_crd_check_git_depends(self):
  349. "Test single-repo dependencies in independent pipelines"
  350. self.gearman_server.hold_jobs_in_build = True
  351. A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
  352. B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
  353. # Add two git-dependent changes and make sure they both report
  354. # success.
  355. B.setDependsOn(A, 1)
  356. self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
  357. self.waitUntilSettled()
  358. self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
  359. self.waitUntilSettled()
  360. self.orderedRelease()
  361. self.gearman_server.hold_jobs_in_build = False
  362. self.waitUntilSettled()
  363. self.assertEqual(A.data['status'], 'NEW')
  364. self.assertEqual(B.data['status'], 'NEW')
  365. self.assertEqual(A.reported, 1)
  366. self.assertEqual(B.reported, 1)
  367. self.assertEqual(self.history[0].changes, '1,1')
  368. self.assertEqual(self.history[-1].changes, '1,1 2,1')
  369. tenant = self.scheds.first.sched.abide.tenants.get('tenant-one')
  370. self.assertEqual(len(tenant.layout.pipelines['check'].queues), 0)
  371. self.assertIn('Build succeeded', A.messages[0])
  372. self.assertIn('Build succeeded', B.messages[0])
  373. def test_crd_check_duplicate(self):
  374. "Test duplicate check in independent pipelines"
  375. self.executor_server.hold_jobs_in_build = True
  376. A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
  377. B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
  378. tenant = self.scheds.first.sched.abide.tenants.get('tenant-one')
  379. check_pipeline = tenant.layout.pipelines['check']
  380. # Add two git-dependent changes...
  381. B.setDependsOn(A, 1)
  382. self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
  383. self.waitUntilSettled()
  384. self.assertEqual(len(check_pipeline.getAllItems()), 2)
  385. # ...make sure the live one is not duplicated...
  386. self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
  387. self.waitUntilSettled()
  388. self.assertEqual(len(check_pipeline.getAllItems()), 2)
  389. # ...but the non-live one is able to be.
  390. self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
  391. self.waitUntilSettled()
  392. self.assertEqual(len(check_pipeline.getAllItems()), 3)
  393. # Release jobs in order to avoid races with change A jobs
  394. # finishing before change B jobs.
  395. self.orderedRelease()
  396. self.executor_server.hold_jobs_in_build = False
  397. self.executor_server.release()
  398. self.waitUntilSettled()
  399. self.assertEqual(A.data['status'], 'NEW')
  400. self.assertEqual(B.data['status'], 'NEW')
  401. self.assertEqual(A.reported, 1)
  402. self.assertEqual(B.reported, 1)
  403. self.assertEqual(self.history[0].changes, '1,1 2,1')
  404. self.assertEqual(self.history[1].changes, '1,1')
  405. self.assertEqual(len(tenant.layout.pipelines['check'].queues), 0)
  406. self.assertIn('Build succeeded', A.messages[0])
  407. self.assertIn('Build succeeded', B.messages[0])
  408. def _test_crd_check_reconfiguration(self, project1, project2):
  409. "Test cross-repo dependencies re-enqueued in independent pipelines"
  410. self.gearman_server.hold_jobs_in_queue = True
  411. A = self.fake_gerrit.addFakeChange(project1, 'master', 'A')
  412. B = self.fake_gerrit.addFakeChange(project2, 'master', 'B')
  413. # A Depends-On: B
  414. A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  415. A.subject, B.data['url'])
  416. self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
  417. self.waitUntilSettled()
  418. self.scheds.execute(lambda app: app.sched.reconfigure(app.config))
  419. # Make sure the items still share a change queue, and the
  420. # first one is not live.
  421. tenant = self.scheds.first.sched.abide.tenants.get('tenant-one')
  422. self.assertEqual(len(tenant.layout.pipelines['check'].queues), 1)
  423. queue = tenant.layout.pipelines['check'].queues[0]
  424. first_item = queue.queue[0]
  425. for item in queue.queue:
  426. self.assertEqual(item.queue, first_item.queue)
  427. self.assertFalse(first_item.live)
  428. self.assertTrue(queue.queue[1].live)
  429. self.gearman_server.hold_jobs_in_queue = False
  430. self.gearman_server.release()
  431. self.waitUntilSettled()
  432. self.assertEqual(A.data['status'], 'NEW')
  433. self.assertEqual(B.data['status'], 'NEW')
  434. self.assertEqual(A.reported, 1)
  435. self.assertEqual(B.reported, 0)
  436. self.assertEqual(self.history[0].changes, '2,1 1,1')
  437. self.assertEqual(len(tenant.layout.pipelines['check'].queues), 0)
  438. def test_crd_check_reconfiguration(self):
  439. self._test_crd_check_reconfiguration('org/project1', 'org/project2')
  440. def test_crd_undefined_project(self):
  441. """Test that undefined projects in dependencies are handled for
  442. independent pipelines"""
  443. # It's a hack for fake gerrit,
  444. # as it implies repo creation upon the creation of any change
  445. self.init_repo("org/unknown", tag='init')
  446. self._test_crd_check_reconfiguration('org/project1', 'org/unknown')
  447. @simple_layout('layouts/ignore-dependencies.yaml')
  448. def test_crd_check_ignore_dependencies(self):
  449. "Test cross-repo dependencies can be ignored"
  450. self.gearman_server.hold_jobs_in_queue = True
  451. A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
  452. B = self.fake_gerrit.addFakeChange('org/project2', 'master', 'B')
  453. C = self.fake_gerrit.addFakeChange('org/project2', 'master', 'C')
  454. # A Depends-On: B
  455. A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  456. A.subject, B.data['url'])
  457. # C git-depends on B
  458. C.setDependsOn(B, 1)
  459. self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
  460. self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
  461. self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(1))
  462. self.waitUntilSettled()
  463. # Make sure none of the items share a change queue, and all
  464. # are live.
  465. tenant = self.scheds.first.sched.abide.tenants.get('tenant-one')
  466. check_pipeline = tenant.layout.pipelines['check']
  467. self.assertEqual(len(check_pipeline.queues), 3)
  468. self.assertEqual(len(check_pipeline.getAllItems()), 3)
  469. for item in check_pipeline.getAllItems():
  470. self.assertTrue(item.live)
  471. self.gearman_server.hold_jobs_in_queue = False
  472. self.gearman_server.release()
  473. self.waitUntilSettled()
  474. self.assertEqual(A.data['status'], 'NEW')
  475. self.assertEqual(B.data['status'], 'NEW')
  476. self.assertEqual(C.data['status'], 'NEW')
  477. self.assertEqual(A.reported, 1)
  478. self.assertEqual(B.reported, 1)
  479. self.assertEqual(C.reported, 1)
  480. # Each job should have tested exactly one change
  481. for job in self.history:
  482. self.assertEqual(len(job.changes.split()), 1)
  483. def test_crd_check_triangle(self):
  484. A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
  485. B = self.fake_gerrit.addFakeChange('org/project2', 'master', 'B')
  486. C = self.fake_gerrit.addFakeChange('org/project2', 'master', 'C')
  487. # C-->B
  488. # \ /
  489. # v
  490. # A
  491. # C Depends-On: A
  492. C.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  493. C.subject, A.data['url'])
  494. # B Depends-On: A
  495. B.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  496. B.subject, A.data['url'])
  497. # C git-depends on B
  498. C.setDependsOn(B, 1)
  499. self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(1))
  500. self.waitUntilSettled()
  501. self.assertEqual(C.reported, 1)
  502. self.assertEqual(self.history[0].changes, '1,1 2,1 3,1')
  503. @simple_layout('layouts/three-projects.yaml')
  504. def test_crd_check_transitive(self):
  505. "Test transitive cross-repo dependencies"
  506. # Specifically, if A -> B -> C, and C gets a new patchset and
  507. # A gets a new patchset, ensure the test of A,2 includes B,1
  508. # and C,2 (not C,1 which would indicate stale data in the
  509. # cache for B).
  510. A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
  511. B = self.fake_gerrit.addFakeChange('org/project2', 'master', 'B')
  512. C = self.fake_gerrit.addFakeChange('org/project3', 'master', 'C')
  513. # A Depends-On: B
  514. A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  515. A.subject, B.data['url'])
  516. # B Depends-On: C
  517. B.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  518. B.subject, C.data['url'])
  519. self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
  520. self.waitUntilSettled()
  521. self.assertEqual(self.history[-1].changes, '3,1 2,1 1,1')
  522. self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
  523. self.waitUntilSettled()
  524. self.assertEqual(self.history[-1].changes, '3,1 2,1')
  525. self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(1))
  526. self.waitUntilSettled()
  527. self.assertEqual(self.history[-1].changes, '3,1')
  528. C.addPatchset()
  529. self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(2))
  530. self.waitUntilSettled()
  531. self.assertEqual(self.history[-1].changes, '3,2')
  532. A.addPatchset()
  533. self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(2))
  534. self.waitUntilSettled()
  535. self.assertEqual(self.history[-1].changes, '3,2 2,1 1,2')
  536. def test_crd_check_unknown(self):
  537. "Test unknown projects in independent pipeline"
  538. self.init_repo("org/unknown", tag='init')
  539. A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
  540. B = self.fake_gerrit.addFakeChange('org/unknown', 'master', 'D')
  541. # A Depends-On: B
  542. A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  543. A.subject, B.data['url'])
  544. # Make sure zuul has seen an event on B.
  545. self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
  546. self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
  547. self.waitUntilSettled()
  548. self.assertEqual(A.data['status'], 'NEW')
  549. self.assertEqual(A.reported, 1)
  550. self.assertEqual(B.data['status'], 'NEW')
  551. self.assertEqual(B.reported, 0)
  552. def test_crd_cycle_join(self):
  553. "Test an updated change creates a cycle"
  554. A = self.fake_gerrit.addFakeChange('org/project2', 'master', 'A')
  555. self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
  556. self.waitUntilSettled()
  557. self.assertEqual(A.reported, 1)
  558. # Create B->A
  559. B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
  560. B.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  561. B.subject, A.data['url'])
  562. self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
  563. self.waitUntilSettled()
  564. # Dep is there so zuul should have reported on B
  565. self.assertEqual(B.reported, 1)
  566. # Update A to add A->B (a cycle).
  567. A.addPatchset()
  568. A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  569. A.subject, B.data['url'])
  570. self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(2))
  571. self.waitUntilSettled()
  572. # Dependency cycle injected so zuul should have reported again on A
  573. self.assertEqual(A.reported, 2)
  574. # Now if we update B to remove the depends-on, everything
  575. # should be okay. B; A->B
  576. B.addPatchset()
  577. B.data['commitMessage'] = '%s\n' % (B.subject,)
  578. self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(2))
  579. self.waitUntilSettled()
  580. # Cycle was removed so now zuul should have reported again on A
  581. self.assertEqual(A.reported, 3)
  582. self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(2))
  583. self.waitUntilSettled()
  584. self.assertEqual(B.reported, 2)
  585. class TestGerritCRDAltBaseUrl(ZuulTestCase):
  586. tenant_config_file = 'config/single-tenant/main.yaml'
  587. def setup_config(self, config_file: str):
  588. config = super(TestGerritCRDAltBaseUrl, self).setup_config(config_file)
  589. self.baseurl = 'https://review.example.com/prefixed_gerrit_ui/'
  590. config.set('connection gerrit', 'baseurl', self.baseurl)
  591. return config
  592. def test_basic_crd_check(self):
  593. "Test basic cross-repo dependencies with an alternate gerrit baseurl"
  594. self.executor_server.hold_jobs_in_build = True
  595. self.gearman_server.hold_jobs_in_queue = True
  596. A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
  597. B = self.fake_gerrit.addFakeChange('org/project2', 'master', 'B')
  598. self.assertEqual(B.data['url'], '%s/2' % self.baseurl.rstrip('/'))
  599. # A Depends-On: B
  600. A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
  601. A.subject, B.data['url'])
  602. self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
  603. self.waitUntilSettled()
  604. self.gearman_server.hold_jobs_in_queue = False
  605. self.gearman_server.release()
  606. self.waitUntilSettled()
  607. self.executor_server.release('.*-merge')
  608. self.waitUntilSettled()
  609. self.assertTrue(self.builds[0].hasChanges(A, B))
  610. self.executor_server.hold_jobs_in_build = False
  611. self.executor_server.release()
  612. self.waitUntilSettled()
  613. self.assertEqual(A.data['status'], 'NEW')
  614. self.assertEqual(B.data['status'], 'NEW')
  615. self.assertEqual(A.reported, 1)
  616. self.assertEqual(B.reported, 0)
  617. self.assertEqual(self.history[0].changes, '2,1 1,1')
  618. tenant = self.scheds.first.sched.abide.tenants.get('tenant-one')
  619. self.assertEqual(len(tenant.layout.pipelines['check'].queues), 0)
  620. class TestGerritCRDWeb(TestGerritCRD):
  621. config_file = 'zuul-gerrit-web.conf'