
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
201 lines
7.9 KiB
Python
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'))
|