diff options
Diffstat (limited to 'tests/unit/test_global_semaphores.py')
-rw-r--r-- | tests/unit/test_global_semaphores.py | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/tests/unit/test_global_semaphores.py b/tests/unit/test_global_semaphores.py new file mode 100644 index 000000000..b76f9d812 --- /dev/null +++ b/tests/unit/test_global_semaphores.py @@ -0,0 +1,169 @@ +# Copyright 2022 Acme Gating, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import zuul.configloader + +from tests.base import ZuulTestCase + + +class TestGlobalSemaphoresConfig(ZuulTestCase): + tenant_config_file = 'config/global-semaphores-config/main.yaml' + + def assertSemaphores(self, tenant, semaphores): + for k, v in semaphores.items(): + self.assertEqual( + len(tenant.semaphore_handler.semaphoreHolders(k)), + v, k) + + def assertSemaphoresMax(self, tenant, semaphores): + for k, v in semaphores.items(): + abide = tenant.semaphore_handler.abide + semaphore = tenant.layout.getSemaphore(abide, k) + self.assertEqual(semaphore.max, v, k) + + def test_semaphore_scope(self): + # This tests global and tenant semaphore scope + self.executor_server.hold_jobs_in_build = True + tenant1 = self.scheds.first.sched.abide.tenants.get('tenant-one') + tenant2 = self.scheds.first.sched.abide.tenants.get('tenant-two') + tenant3 = self.scheds.first.sched.abide.tenants.get('tenant-three') + + # The different max values will tell us that we have the right + # semaphore objects. Each tenant has one tenant-scope + # semaphore in a tenant-specific project, and one tenant-scope + # semaphore with a common definition. Tenants 1 and 2 share a + # global-scope semaphore, and tenant 3 has a tenant-scope + # semaphore with the same name. + + # Here is what is defined in each tenant: + # Tenant-one: + # * global-semaphore: scope:global max:100 definition:main.yaml + # * common-semaphore: scope:tenant max:10 definition:common-config + # * project1-semaphore: scope:tenant max:11 definition:project1 + # * (global-semaphore): scope:tenant max:2 definition:project1 + # [unused since it shadows the actual global-semaphore] + # Tenant-two: + # * global-semaphore: scope:global max:100 definition:main.yaml + # * common-semaphore: scope:tenant max:10 definition:common-config + # * project2-semaphore: scope:tenant max:12 definition:project2 + # Tenant-three: + # * global-semaphore: scope:global max:999 definition:project3 + # * common-semaphore: scope:tenant max:10 definition:common-config + # * project3-semaphore: scope:tenant max:13 definition:project3 + self.assertSemaphoresMax(tenant1, {'global-semaphore': 100, + 'common-semaphore': 10, + 'project1-semaphore': 11, + 'project2-semaphore': 1, + 'project3-semaphore': 1}) + self.assertSemaphoresMax(tenant2, {'global-semaphore': 100, + 'common-semaphore': 10, + 'project1-semaphore': 1, + 'project2-semaphore': 12, + 'project3-semaphore': 1}) + # This "global" semaphore is really tenant-scoped, it just has + # the same name. + self.assertSemaphoresMax(tenant3, {'global-semaphore': 999, + 'common-semaphore': 10, + 'project1-semaphore': 1, + 'project2-semaphore': 1, + 'project3-semaphore': 13}) + + # We should have a config error in tenant1 due to the + # redefinition. + self.assertEquals(len(tenant1.layout.loading_errors), 1) + self.assertEquals(len(tenant2.layout.loading_errors), 0) + self.assertEquals(len(tenant3.layout.loading_errors), 0) + + A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A') + B = self.fake_gerrit.addFakeChange('org/project2', 'master', 'B') + C = self.fake_gerrit.addFakeChange('org/project3', 'master', 'C') + self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) + self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1)) + self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + + # Checking the number of holders tells us whethere we are + # using global or tenant-scoped semaphores. Each in-use + # semaphore in a tenant should have only one holder except the + # global-scope semaphore shared between tenants 1 and 2. + self.assertSemaphores(tenant1, {'global-semaphore': 2, + 'common-semaphore': 1, + 'project1-semaphore': 1, + 'project2-semaphore': 0, + 'project3-semaphore': 0}) + self.assertSemaphores(tenant2, {'global-semaphore': 2, + 'common-semaphore': 1, + 'project1-semaphore': 0, + 'project2-semaphore': 1, + 'project3-semaphore': 0}) + self.assertSemaphores(tenant3, {'global-semaphore': 1, + 'common-semaphore': 1, + 'project1-semaphore': 0, + 'project2-semaphore': 0, + 'project3-semaphore': 1}) + + self.executor_server.hold_jobs_in_build = False + self.executor_server.release() + self.waitUntilSettled() + + +class TestGlobalSemaphoresBroken(ZuulTestCase): + validate_tenants = [] + tenant_config_file = 'config/global-semaphores-config/broken.yaml' + # This test raises a config error during the startup of the test + # case which makes the first scheduler fail during its startup. + # The second (or any additional) scheduler won't even run as the + # startup is serialized in tests/base.py. + # Thus it doesn't make sense to execute this test with multiple + # schedulers. + scheduler_count = 1 + + def setUp(self): + self.assertRaises(zuul.configloader.GlobalSemaphoreNotFoundError, + super().setUp) + + def test_broken_global_semaphore_config(self): + pass + + +class TestGlobalSemaphores(ZuulTestCase): + tenant_config_file = 'config/global-semaphores/main.yaml' + + def test_global_semaphores(self): + # This tests that a job finishing in one tenant will correctly + # start a job in another tenant waiting on the semahpore. + self.executor_server.hold_jobs_in_build = True + A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A') + self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + + B = self.fake_gerrit.addFakeChange('org/project2', 'master', 'B') + self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + + self.assertHistory([]) + self.assertBuilds([ + dict(name='test-global-semaphore', changes='1,1'), + ]) + + self.executor_server.hold_jobs_in_build = False + self.executor_server.release() + self.waitUntilSettled() + + self.assertHistory([ + dict(name='test-global-semaphore', + result='SUCCESS', changes='1,1'), + dict(name='test-global-semaphore', + result='SUCCESS', changes='2,1'), + ], ordered=False) |