summaryrefslogtreecommitdiff
path: root/tests/unit/test_global_semaphores.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unit/test_global_semaphores.py')
-rw-r--r--tests/unit/test_global_semaphores.py169
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)