Files
zuul/tests/unit/test_git_driver.py
James E. Blair cc509f2b13 Add newrev to timer-triggered items
If jobs in a periodic pipeline run longer than the timer interval
for the pipeline trigger, Zuul will not enqueue a second queue
item for the same project-branch.  This can be counter-intuitive
for users, where a pipeline that runs every 24 hours may turn into
a pipeline that runs every 48 hours if a job happens to take 25
hours.

To address this, we will now store the branch head sha as the
newrev of the ref associated with queue items.  This will cause
periodic pipelines to behave a bit more like post pipelines, in
that multiple items will be allowed for the same project-branch.
However, if the branch sha has not changed, we will not enqueue
further items (since they will continue to look identical to
Zuul).

Change-Id: I41891ac3011fb95c4c891c7554e1ea6bec94b56f
2025-05-14 16:49:29 -07:00

201 lines
7.9 KiB
Python

# Copyright 2016 Red Hat, Inc.
#
# 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 os
import time
import yaml
from tests.base import ZuulTestCase, simple_layout
class TestGitDriver(ZuulTestCase):
config_file = 'zuul-git-driver.conf'
tenant_config_file = 'config/git-driver/main.yaml'
def setUp(self):
super(TestGitDriver, self).setUp()
self.git_connection = self.scheds.first.sched.connections\
.getSource('git').connection
def setup_config(self, config_file: str):
config = super(TestGitDriver, self).setup_config(config_file)
config.set('connection git', 'baseurl', self.upstream_root)
return config
def test_basic(self):
tenant = self.scheds.first.sched.abide.tenants.get('tenant-one')
# Check that we have the git source for common-config and the
# gerrit source for the project.
self.assertEqual('git', list(tenant.config_projects)[0].source.name)
self.assertEqual('common-config', list(tenant.config_projects)[0].name)
self.assertEqual('gerrit',
list(tenant.untrusted_projects)[0].source.name)
self.assertEqual('org/project',
list(tenant.untrusted_projects)[0].name)
# The configuration for this test is accessed via the git
# driver (in common-config), rather than the gerrit driver, so
# if the job runs, it worked.
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(A.reported, 1)
def test_config_refreshed(self):
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(A.reported, 1)
self.assertEqual(self.history[0].name, 'project-test1')
# Update zuul.yaml to force a tenant reconfiguration
path = os.path.join(self.upstream_root, 'common-config', 'zuul.yaml')
with open(path, 'r') as f:
config = yaml.safe_load(f)
change = {
'name': 'org/project',
'check': {
'jobs': [
'project-test2'
]
}
}
config[4]['project'] = change
files = {'zuul.yaml': yaml.dump(config)}
self.addCommitToRepo(
'common-config', 'Change zuul.yaml configuration', files)
# Wait for the tenant reconfiguration to happen
count = self.waitForEvent()
self.waitUntilSettled()
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(len(self.history), 2)
self.assertEqual(A.reported, 1)
# We make sure the new job has run
self.assertEqual(self.history[1].name, 'project-test2')
# Let's stop the git Watcher to let us merge some changes commits
# We want to verify that config changes are detected for commits
# on the range oldrev..newrev
self.scheds.first.sched.connections.getSource('git').connection\
.watcher_thread._pause = True
# Add a config change
change = {
'name': 'org/project',
'check': {
'jobs': [
'project-test1'
]
}
}
config[4]['project'] = change
files = {'zuul.yaml': yaml.dump(config)}
self.addCommitToRepo(
'common-config', 'Change zuul.yaml configuration', files)
# Add two other changes
self.addCommitToRepo(
'common-config', 'Adding f1',
{'f1': "Content"})
self.addCommitToRepo(
'common-config', 'Adding f2',
{'f2': "Content"})
# Restart the git watcher
self.scheds.first.sched.connections.getSource('git').connection\
.watcher_thread._pause = False
# Wait for the tenant reconfiguration to happen
self.waitForEvent(count)
self.waitUntilSettled()
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(len(self.history), 3)
self.assertEqual(A.reported, 1)
# We make sure the new job has run
self.assertEqual(self.history[2].name, 'project-test1')
def ensure_watcher_has_context(self):
# Make sure watcher have read initial refs shas
delay = 0.1
max_delay = 1
while not self.git_connection.watcher_thread.projects_refs:
time.sleep(delay)
max_delay -= delay
if max_delay <= 0:
raise Exception("Timeout waiting for initial read")
return self.git_connection.watcher_thread._event_count
def waitForEvent(self, initial_count=0):
delay = 0.1
max_delay = 5
while self.git_connection.watcher_thread._event_count <= initial_count:
time.sleep(delay)
max_delay -= delay
if max_delay <= 0:
raise Exception("Timeout waiting for event")
return self.git_connection.watcher_thread._event_count
@simple_layout('layouts/basic-git.yaml', driver='git')
def test_ref_updated_event(self):
count = self.ensure_watcher_has_context()
# Add a commit to trigger a ref-updated event
self.addCommitToRepo(
'org/project', 'A change for ref-updated', {'f1': 'Content'})
# Wait for the git watcher to detect the ref-update event
self.waitForEvent(count)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual('SUCCESS',
self.getJobFromHistory('post-job').result)
@simple_layout('layouts/basic-git.yaml', driver='git')
def test_ref_created(self):
count = self.ensure_watcher_has_context()
# Tag HEAD to trigger a ref-updated event
self.addTagToRepo(
'org/project', 'atag', 'HEAD')
# Wait for the git watcher to detect the ref-update event
self.waitForEvent(count)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual('SUCCESS',
self.getJobFromHistory('tag-job').result)
@simple_layout('layouts/basic-git.yaml', driver='git')
def test_ref_deleted(self):
count = self.ensure_watcher_has_context()
# Delete default tag init to trigger a ref-updated event
self.delTagFromRepo(
'org/project', 'init')
# Wait for the git watcher to detect the ref-update event
self.waitForEvent(count)
self.waitUntilSettled()
# Make sure no job as run as ignore-delete is True by default
self.assertEqual(len(self.history), 0)
@simple_layout('layouts/basic-git.yaml', driver='git')
def test_get_project_branch_sha(self):
# Exercise this method since it's only called from timer
# triggers
source = self.scheds.first.sched.connections.getSource('git')
project = source.getProject('org/project')
self.assertIsNotNone(source.getProjectBranchSha(project, 'master'))