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.
 
 
 

4664 lines
179 KiB

# Copyright 2012 Hewlett-Packard Development Company, L.P.
#
# 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 io
import json
import logging
import os
import textwrap
import gc
import time
from unittest import skip
import paramiko
import zuul.configloader
from zuul.lib import encryption
from tests.base import (
AnsibleZuulTestCase,
ZuulTestCase,
FIXTURE_DIR,
simple_layout,
)
class TestMultipleTenants(AnsibleZuulTestCase):
# A temporary class to hold new tests while others are disabled
tenant_config_file = 'config/multi-tenant/main.yaml'
def test_multiple_tenants(self):
A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(self.getJobFromHistory('project1-test1').result,
'SUCCESS')
self.assertEqual(self.getJobFromHistory('python27').result,
'SUCCESS')
self.assertEqual(A.data['status'], 'MERGED')
self.assertEqual(A.reported, 2,
"A should report start and success")
self.assertIn('tenant-one-gate', A.messages[1],
"A should transit tenant-one gate")
self.assertNotIn('tenant-two-gate', A.messages[1],
"A should *not* transit tenant-two gate")
B = self.fake_gerrit.addFakeChange('org/project2', 'master', 'B')
B.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(self.getJobFromHistory('python27',
'org/project2').result,
'SUCCESS')
self.assertEqual(self.getJobFromHistory('project2-test1').result,
'SUCCESS')
self.assertEqual(B.data['status'], 'MERGED')
self.assertEqual(B.reported, 2,
"B should report start and success")
self.assertIn('tenant-two-gate', B.messages[1],
"B should transit tenant-two gate")
self.assertNotIn('tenant-one-gate', B.messages[1],
"B should *not* transit tenant-one gate")
self.assertEqual(A.reported, 2, "Activity in tenant two should"
"not affect tenant one")
class TestProtected(ZuulTestCase):
tenant_config_file = 'config/protected/main.yaml'
def test_protected_ok(self):
# test clean usage of final parent job
in_repo_conf = textwrap.dedent(
"""
- job:
name: job-protected
protected: true
run: playbooks/job-protected.yaml
- project:
name: org/project
check:
jobs:
- job-child-ok
- job:
name: job-child-ok
parent: job-protected
- project:
name: org/project
check:
jobs:
- job-child-ok
""")
file_dict = {'zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(A.reported, 1)
self.assertEqual(A.patchsets[-1]['approvals'][0]['value'], '1')
def test_protected_reset(self):
# try to reset protected flag
in_repo_conf = textwrap.dedent(
"""
- job:
name: job-protected
protected: true
run: playbooks/job-protected.yaml
- job:
name: job-child-reset-protected
parent: job-protected
protected: false
- project:
name: org/project
check:
jobs:
- job-child-reset-protected
""")
file_dict = {'zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
# The second patch tried to override some variables.
# Thus it should fail.
self.assertEqual(A.reported, 1)
self.assertEqual(A.patchsets[-1]['approvals'][0]['value'], '-1')
self.assertIn('Unable to reset protected attribute', A.messages[0])
def test_protected_inherit_not_ok(self):
# try to inherit from a protected job in different project
in_repo_conf = textwrap.dedent(
"""
- job:
name: job-child-notok
run: playbooks/job-child-notok.yaml
parent: job-protected
- project:
name: org/project1
check:
jobs:
- job-child-notok
""")
file_dict = {'zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(A.reported, 1)
self.assertEqual(A.patchsets[-1]['approvals'][0]['value'], '-1')
self.assertIn(
"which is defined in review.example.com/org/project is protected "
"and cannot be inherited from other projects.", A.messages[0])
class TestAbstract(ZuulTestCase):
tenant_config_file = 'config/abstract/main.yaml'
def test_abstract_fail(self):
in_repo_conf = textwrap.dedent(
"""
- project:
check:
jobs:
- job-abstract
""")
file_dict = {'zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(A.reported, 1)
self.assertEqual(A.patchsets[-1]['approvals'][0]['value'], '-1')
self.assertIn('may not be directly run', A.messages[0])
def test_child_of_abstract(self):
in_repo_conf = textwrap.dedent(
"""
- project:
check:
jobs:
- job-child
""")
file_dict = {'zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(A.reported, 1)
self.assertEqual(A.patchsets[-1]['approvals'][0]['value'], '1')
class TestFinal(ZuulTestCase):
tenant_config_file = 'config/final/main.yaml'
def test_final_variant_ok(self):
# test clean usage of final parent job
in_repo_conf = textwrap.dedent(
"""
- project:
name: org/project
check:
jobs:
- job-final
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(A.reported, 1)
self.assertEqual(A.patchsets[-1]['approvals'][0]['value'], '1')
def test_final_variant_error(self):
# test misuse of final parent job
in_repo_conf = textwrap.dedent(
"""
- project:
name: org/project
check:
jobs:
- job-final:
vars:
dont_override_this: bar
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
# The second patch tried to override some variables.
# Thus it should fail.
self.assertEqual(A.reported, 1)
self.assertEqual(A.patchsets[-1]['approvals'][0]['value'], '-1')
self.assertIn('Unable to modify final job', A.messages[0])
def test_final_inheritance(self):
# test misuse of final parent job
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test
parent: job-final
run: playbooks/project-test.yaml
- project:
name: org/project
check:
jobs:
- project-test
""")
in_repo_playbook = textwrap.dedent(
"""
- hosts: all
tasks: []
""")
file_dict = {'.zuul.yaml': in_repo_conf,
'playbooks/project-test.yaml': in_repo_playbook}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
# The second patch tried to override some variables.
# Thus it should fail.
self.assertEqual(A.reported, 1)
self.assertEqual(A.patchsets[-1]['approvals'][0]['value'], '-1')
self.assertIn('Unable to modify final job', A.messages[0])
class TestBranchDeletion(ZuulTestCase):
tenant_config_file = 'config/branch-deletion/main.yaml'
def test_branch_delete(self):
# This tests a tenant reconfiguration on deleting a branch
# *after* an earlier failed tenant reconfiguration. This
# ensures that cached data are appropriately removed, even if
# we are recovering from an invalid config.
self.create_branch('org/project', 'stable/queens')
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'org/project', 'stable/queens'))
self.waitUntilSettled()
in_repo_conf = textwrap.dedent(
"""
- project:
check:
jobs:
- nonexistent-job
""")
file_dict = {'zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'stable/queens', 'A',
files=file_dict)
A.setMerged()
self.fake_gerrit.addEvent(A.getChangeMergedEvent())
self.waitUntilSettled()
self.delete_branch('org/project', 'stable/queens')
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchDeletedEvent(
'org/project', 'stable/queens'))
self.waitUntilSettled()
in_repo_conf = textwrap.dedent(
"""
- project:
check:
jobs:
- base
""")
file_dict = {'zuul.yaml': in_repo_conf}
B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B',
files=file_dict)
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(B.reported, 1)
self.assertHistory([
dict(name='base', result='SUCCESS', changes='2,1')])
def test_branch_delete_full_reconfiguration(self):
# This tests a full configuration after deleting a branch
# *after* an earlier failed tenant reconfiguration. This
# ensures that cached data are appropriately removed, even if
# we are recovering from an invalid config.
self.create_branch('org/project', 'stable/queens')
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'org/project', 'stable/queens'))
self.waitUntilSettled()
in_repo_conf = textwrap.dedent(
"""
- project:
check:
jobs:
- nonexistent-job
""")
file_dict = {'zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'stable/queens', 'A',
files=file_dict)
A.setMerged()
self.fake_gerrit.addEvent(A.getChangeMergedEvent())
self.waitUntilSettled()
self.delete_branch('org/project', 'stable/queens')
self.sched.reconfigure(self.config)
self.waitUntilSettled()
in_repo_conf = textwrap.dedent(
"""
- project:
check:
jobs:
- base
""")
file_dict = {'zuul.yaml': in_repo_conf}
B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B',
files=file_dict)
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(B.reported, 1)
self.assertHistory([
dict(name='base', result='SUCCESS', changes='2,1')])
class TestBranchTag(ZuulTestCase):
tenant_config_file = 'config/branch-tag/main.yaml'
def test_no_branch_match(self):
# Test that tag jobs run with no explicit branch matchers
event = self.fake_gerrit.addFakeTag('org/project', 'master', 'foo')
self.fake_gerrit.addEvent(event)
self.waitUntilSettled()
self.assertHistory([
dict(name='central-job', result='SUCCESS', ref='refs/tags/foo'),
dict(name='test-job', result='SUCCESS', ref='refs/tags/foo')],
ordered=False)
def test_no_branch_match_multi_branch(self):
# Test that tag jobs run with no explicit branch matchers in a
# multi-branch project (where jobs generally get implied
# branch matchers)
self.create_branch('org/project', 'stable/pike')
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'org/project', 'stable/pike'))
self.waitUntilSettled()
event = self.fake_gerrit.addFakeTag('org/project', 'master', 'foo')
self.fake_gerrit.addEvent(event)
self.waitUntilSettled()
# test-job does not run in this case because it is defined in
# a branched repo with implied branch matchers. A release job
# defined in a multi-branch repo would need at least one
# top-level variant with no branch matcher in order to match a
# tag.
self.assertHistory([
dict(name='central-job', result='SUCCESS', ref='refs/tags/foo')])
class TestBranchNegative(ZuulTestCase):
tenant_config_file = 'config/branch-negative/main.yaml'
def test_negative_branch_match(self):
# Test that a negative branch matcher works with implied branches.
self.create_branch('org/project', 'stable/pike')
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'org/project', 'stable/pike'))
self.waitUntilSettled()
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
B = self.fake_gerrit.addFakeChange('org/project', 'stable/pike', 'A')
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertHistory([
dict(name='test-job', result='SUCCESS', changes='1,1')])
class TestBranchTemplates(ZuulTestCase):
tenant_config_file = 'config/branch-templates/main.yaml'
def test_template_removal_from_branch(self):
# Test that a template can be removed from one branch but not
# another.
# This creates a new branch with a copy of the config in master
self.create_branch('puppet-integration', 'stable/newton')
self.create_branch('puppet-integration', 'stable/ocata')
self.create_branch('puppet-tripleo', 'stable/newton')
self.create_branch('puppet-tripleo', 'stable/ocata')
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'puppet-integration', 'stable/newton'))
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'puppet-integration', 'stable/ocata'))
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'puppet-tripleo', 'stable/newton'))
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'puppet-tripleo', 'stable/ocata'))
self.waitUntilSettled()
in_repo_conf = textwrap.dedent(
"""
- project:
name: puppet-tripleo
check:
jobs:
- puppet-something
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('puppet-tripleo', 'stable/newton',
'A', files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertHistory([
dict(name='puppet-something', result='SUCCESS', changes='1,1')])
def test_template_change_on_branch(self):
# Test that the contents of a template can be changed on one
# branch without affecting another.
# This creates a new branch with a copy of the config in master
self.create_branch('puppet-integration', 'stable/newton')
self.create_branch('puppet-integration', 'stable/ocata')
self.create_branch('puppet-tripleo', 'stable/newton')
self.create_branch('puppet-tripleo', 'stable/ocata')
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'puppet-integration', 'stable/newton'))
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'puppet-integration', 'stable/ocata'))
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'puppet-tripleo', 'stable/newton'))
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'puppet-tripleo', 'stable/ocata'))
self.waitUntilSettled()
in_repo_conf = textwrap.dedent("""
- job:
name: puppet-unit-base
run: playbooks/run-unit-tests.yaml
- job:
name: puppet-unit-3.8
parent: puppet-unit-base
branches: ^(stable/(newton|ocata)).*$
vars:
puppet_gem_version: 3.8
- job:
name: puppet-something
run: playbooks/run-unit-tests.yaml
- project-template:
name: puppet-unit
check:
jobs:
- puppet-something
- project:
name: puppet-integration
templates:
- puppet-unit
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('puppet-integration',
'stable/newton',
'A', files=file_dict)
B = self.fake_gerrit.addFakeChange('puppet-tripleo',
'stable/newton',
'B')
B.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
B.subject, A.data['id'])
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertHistory([
dict(name='puppet-something', result='SUCCESS',
changes='1,1 2,1')])
class TestBranchVariants(ZuulTestCase):
tenant_config_file = 'config/branch-variants/main.yaml'
def test_branch_variants(self):
# Test branch variants of jobs with inheritance
self.executor_server.hold_jobs_in_build = True
# This creates a new branch with a copy of the config in master
self.create_branch('puppet-integration', 'stable')
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'puppet-integration', 'stable'))
self.waitUntilSettled()
A = self.fake_gerrit.addFakeChange('puppet-integration', 'stable', 'A')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(len(self.builds[0].parameters['pre_playbooks']), 3)
self.executor_server.hold_jobs_in_build = False
self.executor_server.release()
self.waitUntilSettled()
def test_branch_variants_reconfigure(self):
# Test branch variants of jobs with inheritance
self.executor_server.hold_jobs_in_build = True
# This creates a new branch with a copy of the config in master
self.create_branch('puppet-integration', 'stable')
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'puppet-integration', 'stable'))
self.waitUntilSettled()
with open(os.path.join(FIXTURE_DIR,
'config/branch-variants/git/',
'puppet-integration/.zuul.yaml')) as f:
config = f.read()
# Push a change that triggers a dynamic reconfiguration
file_dict = {'.zuul.yaml': config}
A = self.fake_gerrit.addFakeChange('puppet-integration', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
ipath = self.builds[0].parameters['zuul']['_inheritance_path']
for i in ipath:
self.log.debug("inheritance path %s", i)
self.assertEqual(len(ipath), 5)
self.executor_server.hold_jobs_in_build = False
self.executor_server.release()
self.waitUntilSettled()
def test_branch_variants_divergent(self):
# Test branches can diverge and become independent
self.executor_server.hold_jobs_in_build = True
# This creates a new branch with a copy of the config in master
self.create_branch('puppet-integration', 'stable')
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'puppet-integration', 'stable'))
self.waitUntilSettled()
with open(os.path.join(FIXTURE_DIR,
'config/branch-variants/git/',
'puppet-integration/stable.zuul.yaml')) as f:
config = f.read()
file_dict = {'.zuul.yaml': config}
C = self.fake_gerrit.addFakeChange('puppet-integration', 'stable', 'C',
files=file_dict)
C.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
self.waitUntilSettled()
self.fake_gerrit.addEvent(C.getChangeMergedEvent())
self.waitUntilSettled()
A = self.fake_gerrit.addFakeChange('puppet-integration', 'master', 'A')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
B = self.fake_gerrit.addFakeChange('puppet-integration', 'stable', 'B')
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(self.builds[0].parameters['zuul']['jobtags'],
['master'])
self.assertEqual(self.builds[1].parameters['zuul']['jobtags'],
['stable'])
self.executor_server.hold_jobs_in_build = False
self.executor_server.release()
self.waitUntilSettled()
class TestBranchMismatch(ZuulTestCase):
tenant_config_file = 'config/branch-mismatch/main.yaml'
def test_job_override_branch(self):
"Test that override-checkout overrides branch matchers as well"
# Make sure the parent job repo is branched, so it gets
# implied branch matchers.
self.create_branch('org/project1', 'stable')
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'org/project1', 'stable'))
# The child job repo should have a branch which does not exist
# in the parent job repo.
self.create_branch('org/project2', 'devel')
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'org/project2', 'devel'))
# A job in a repo with a weird branch name should use the
# parent job from the parent job's master (default) branch.
A = self.fake_gerrit.addFakeChange('org/project2', 'devel', 'A')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
# project-test2 should run because it inherits from
# project-test1 and we will use the fallback branch to find
# project-test1 variants, but project-test1 itself, even
# though it is in the project-pipeline config, should not run
# because it doesn't directly match.
self.assertHistory([
dict(name='project-test1', result='SUCCESS', changes='1,1'),
dict(name='project-test2', result='SUCCESS', changes='1,1'),
], ordered=False)
class TestAllowedProjects(ZuulTestCase):
tenant_config_file = 'config/allowed-projects/main.yaml'
def test_allowed_projects(self):
A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(A.reported, 1)
self.assertIn('Build succeeded', A.messages[0])
B = self.fake_gerrit.addFakeChange('org/project2', 'master', 'B')
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(B.reported, 1)
self.assertIn('Project org/project2 is not allowed '
'to run job test-project2', B.messages[0])
C = self.fake_gerrit.addFakeChange('org/project3', 'master', 'C')
self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(C.reported, 1)
self.assertIn('Project org/project3 is not allowed '
'to run job restricted-job', C.messages[0])
self.assertHistory([
dict(name='test-project1', result='SUCCESS', changes='1,1'),
dict(name='restricted-job', result='SUCCESS', changes='1,1'),
], ordered=False)
class TestCentralJobs(ZuulTestCase):
tenant_config_file = 'config/central-jobs/main.yaml'
def setUp(self):
super(TestCentralJobs, self).setUp()
self.create_branch('org/project', 'stable')
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'org/project', 'stable'))
self.waitUntilSettled()
def _updateConfig(self, config, branch):
file_dict = {'.zuul.yaml': config}
C = self.fake_gerrit.addFakeChange('org/project', branch, 'C',
files=file_dict)
C.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
self.waitUntilSettled()
self.fake_gerrit.addEvent(C.getChangeMergedEvent())
self.waitUntilSettled()
def _test_central_job_on_branch(self, branch, other_branch):
# Test that a job defined on a branchless repo only runs on
# the branch applied
config = textwrap.dedent(
"""
- project:
name: org/project
check:
jobs:
- central-job
""")
self._updateConfig(config, branch)
A = self.fake_gerrit.addFakeChange('org/project', branch, 'A')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertHistory([
dict(name='central-job', result='SUCCESS', changes='2,1')])
# No jobs should run for this change.
B = self.fake_gerrit.addFakeChange('org/project', other_branch, 'B')
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertHistory([
dict(name='central-job', result='SUCCESS', changes='2,1')])
def test_central_job_on_stable(self):
self._test_central_job_on_branch('master', 'stable')
def test_central_job_on_master(self):
self._test_central_job_on_branch('stable', 'master')
def _test_central_template_on_branch(self, branch, other_branch):
# Test that a project-template defined on a branchless repo
# only runs on the branch applied
config = textwrap.dedent(
"""
- project:
name: org/project
templates: ['central-jobs']
""")
self._updateConfig(config, branch)
A = self.fake_gerrit.addFakeChange('org/project', branch, 'A')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertHistory([
dict(name='central-job', result='SUCCESS', changes='2,1')])
# No jobs should run for this change.
B = self.fake_gerrit.addFakeChange('org/project', other_branch, 'B')
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertHistory([
dict(name='central-job', result='SUCCESS', changes='2,1')])
def test_central_template_on_stable(self):
self._test_central_template_on_branch('master', 'stable')
def test_central_template_on_master(self):
self._test_central_template_on_branch('stable', 'master')
class TestInRepoConfig(ZuulTestCase):
# A temporary class to hold new tests while others are disabled
config_file = 'zuul-connections-gerrit-and-github.conf'
tenant_config_file = 'config/in-repo/main.yaml'
def test_in_repo_config(self):
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(self.getJobFromHistory('project-test1').result,
'SUCCESS')
self.assertEqual(A.data['status'], 'MERGED')
self.assertEqual(A.reported, 2,
"A should report start and success")
self.assertIn('tenant-one-gate', A.messages[1],
"A should transit tenant-one gate")
@skip("This test is useful, but not reliable")
def test_full_and_dynamic_reconfig(self):
self.executor_server.hold_jobs_in_build = True
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
- project:
name: org/project
tenant-one-gate:
jobs:
- project-test1
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.sched.reconfigure(self.config)
self.waitUntilSettled()
gc.collect()
pipelines = [obj for obj in gc.get_objects()
if isinstance(obj, zuul.model.Pipeline)]
self.assertEqual(len(pipelines), 4)
self.executor_server.hold_jobs_in_build = False
self.executor_server.release()
self.waitUntilSettled()
def test_dynamic_config(self):
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
- job:
name: project-test2
run: playbooks/project-test2.yaml
- job:
name: project-test3
run: playbooks/project-test2.yaml
# add a job by the short project name
- project:
name: org/project
tenant-one-gate:
jobs:
- project-test2
# add a job by the canonical project name
- project:
name: review.example.com/org/project
tenant-one-gate:
jobs:
- project-test3
""")
in_repo_playbook = textwrap.dedent(
"""
- hosts: all
tasks: []
""")
file_dict = {'.zuul.yaml': in_repo_conf,
'playbooks/project-test2.yaml': in_repo_playbook}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'MERGED')
self.assertEqual(A.reported, 2,
"A should report start and success")
self.assertIn('tenant-one-gate', A.messages[1],
"A should transit tenant-one gate")
self.assertHistory([
dict(name='project-test2', result='SUCCESS', changes='1,1'),
dict(name='project-test3', result='SUCCESS', changes='1,1'),
], ordered=False)
self.fake_gerrit.addEvent(A.getChangeMergedEvent())
self.waitUntilSettled()
# Now that the config change is landed, it should be live for
# subsequent changes.
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
B.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(self.getJobFromHistory('project-test2').result,
'SUCCESS')
self.assertHistory([
dict(name='project-test2', result='SUCCESS', changes='1,1'),
dict(name='project-test3', result='SUCCESS', changes='1,1'),
dict(name='project-test2', result='SUCCESS', changes='2,1'),
dict(name='project-test3', result='SUCCESS', changes='2,1'),
], ordered=False)
def test_dynamic_template(self):
# Tests that a project can't update a template in another
# project.
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
- project-template:
name: common-config-template
check:
jobs:
- project-test1
- project:
name: org/project
templates: [common-config-template]
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(A.patchsets[0]['approvals'][0]['value'], "-1")
self.assertIn('Project template common-config-template '
'is already defined',
A.messages[0],
"A should have failed the check pipeline")
def test_dynamic_config_errors_not_accumulated(self):
"""Test that requesting broken dynamic configs
does not appear in tenant layout error accumulator"""
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
- project:
name: org/project
check:
jobs:
- non-existent-job
""")
in_repo_playbook = textwrap.dedent(
"""
- hosts: all
tasks: []
""")
file_dict = {'.zuul.yaml': in_repo_conf,
'playbooks/project-test2.yaml': in_repo_playbook}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
tenant = self.sched.abide.tenants.get('tenant-one')
self.assertEquals(
len(tenant.layout.loading_errors), 0,
"No error should have been accumulated")
self.assertHistory([])
def test_dynamic_config_non_existing_job(self):
"""Test that requesting a non existent job fails"""
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
- project:
name: org/project
check:
jobs:
- non-existent-job
""")
in_repo_playbook = textwrap.dedent(
"""
- hosts: all
tasks: []
""")
file_dict = {'.zuul.yaml': in_repo_conf,
'playbooks/project-test2.yaml': in_repo_playbook}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertEqual(A.patchsets[0]['approvals'][0]['value'], "-1")
self.assertIn('Job non-existent-job not defined', A.messages[0],
"A should have failed the check pipeline")
self.assertHistory([])
self.assertEqual(len(A.comments), 1)
comments = sorted(A.comments, key=lambda x: x['line'])
self.assertEqual(comments[0],
{'file': '.zuul.yaml',
'line': 9,
'message': 'Job non-existent-job not defined',
'reviewer': {'email': 'zuul@example.com',
'name': 'Zuul',
'username': 'jenkins'},
'range': {'end_character': 0,
'end_line': 9,
'start_character': 2,
'start_line': 5},
})
def test_dynamic_config_non_existing_job_in_template(self):
"""Test that requesting a non existent job fails"""
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
- project-template:
name: test-template
check:
jobs:
- non-existent-job
- project:
name: org/project
templates:
- test-template
""")
in_repo_playbook = textwrap.dedent(
"""
- hosts: all
tasks: []
""")
file_dict = {'.zuul.yaml': in_repo_conf,
'playbooks/project-test2.yaml': in_repo_playbook}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertEqual(A.patchsets[0]['approvals'][0]['value'], "-1")
self.assertIn('Job non-existent-job not defined', A.messages[0],
"A should have failed the check pipeline")
self.assertHistory([])
def test_dynamic_config_new_patchset(self):
self.executor_server.hold_jobs_in_build = True
tenant = self.sched.abide.tenants.get('tenant-one')
check_pipeline = tenant.layout.pipelines['check']
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
run: playbooks/project-test1.yaml
- job:
name: project-test2
run: playbooks/project-test2.yaml
- project:
name: org/project
check:
jobs:
- project-test2
""")
in_repo_playbook = textwrap.dedent(
"""
- hosts: all
tasks: []
""")
file_dict = {'.zuul.yaml': in_repo_conf,
'playbooks/project-test2.yaml': in_repo_playbook}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
items = check_pipeline.getAllItems()
self.assertEqual(items[0].change.number, '1')
self.assertEqual(items[0].change.patchset, '1')
self.assertTrue(items[0].live)
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
run: playbooks/project-test1.yaml
- job:
name: project-test2
run: playbooks/project-test2.yaml
- project:
name: org/project
check:
jobs:
- project-test1
- project-test2
""")
file_dict = {'.zuul.yaml': in_repo_conf,
'playbooks/project-test2.yaml': in_repo_playbook}
A.addPatchset(files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(2))
self.waitUntilSettled()
items = check_pipeline.getAllItems()
self.assertEqual(items[0].change.number, '1')
self.assertEqual(items[0].change.patchset, '2')
self.assertTrue(items[0].live)
self.executor_server.hold_jobs_in_build = False
self.executor_server.release('project-test1')
self.waitUntilSettled()
self.executor_server.release()
self.waitUntilSettled()
self.assertHistory([
dict(name='project-test2', result='ABORTED', changes='1,1'),
dict(name='project-test1', result='SUCCESS', changes='1,2'),
dict(name='project-test2', result='SUCCESS', changes='1,2')])
def test_in_repo_branch(self):
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
- job:
name: project-test2
run: playbooks/project-test2.yaml
- project:
name: org/project
tenant-one-gate:
jobs:
- project-test2
""")
in_repo_playbook = textwrap.dedent(
"""
- hosts: all
tasks: []
""")
file_dict = {'.zuul.yaml': in_repo_conf,
'playbooks/project-test2.yaml': in_repo_playbook}
self.create_branch('org/project', 'stable')
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'org/project', 'stable'))
self.waitUntilSettled()
A = self.fake_gerrit.addFakeChange('org/project', 'stable', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'MERGED')
self.assertEqual(A.reported, 2,
"A should report start and success")
self.assertIn('tenant-one-gate', A.messages[1],
"A should transit tenant-one gate")
self.assertHistory([
dict(name='project-test2', result='SUCCESS', changes='1,1')])
self.fake_gerrit.addEvent(A.getChangeMergedEvent())
self.waitUntilSettled()
# The config change should not affect master.
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
B.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertHistory([
dict(name='project-test2', result='SUCCESS', changes='1,1'),
dict(name='project-test1', result='SUCCESS', changes='2,1')])
# The config change should be live for further changes on
# stable.
C = self.fake_gerrit.addFakeChange('org/project', 'stable', 'C')
C.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertHistory([
dict(name='project-test2', result='SUCCESS', changes='1,1'),
dict(name='project-test1', result='SUCCESS', changes='2,1'),
dict(name='project-test2', result='SUCCESS', changes='3,1')])
def test_crd_dynamic_config_branch(self):
# Test that we can create a job in one repo and be able to use
# it from a different branch on a different repo.
self.create_branch('org/project1', 'stable')
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'org/project1', 'stable'))
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
- job:
name: project-test2
run: playbooks/project-test2.yaml
- project:
name: org/project
check:
jobs:
- project-test2
""")
in_repo_playbook = textwrap.dedent(
"""
- hosts: all
tasks: []
""")
file_dict = {'.zuul.yaml': in_repo_conf,
'playbooks/project-test2.yaml': in_repo_playbook}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
second_repo_conf = textwrap.dedent(
"""
- project:
name: org/project1
check:
jobs:
- project-test2
""")
second_file_dict = {'.zuul.yaml': second_repo_conf}
B = self.fake_gerrit.addFakeChange('org/project1', 'stable', 'B',
files=second_file_dict)
B.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
B.subject, A.data['id'])
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(A.reported, 1, "A should report")
self.assertHistory([
dict(name='project-test2', result='SUCCESS', changes='1,1'),
dict(name='project-test2', result='SUCCESS', changes='1,1 2,1'),
])
def test_yaml_list_error(self):
in_repo_conf = textwrap.dedent(
"""
job: foo
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('not a list', A.messages[0],
"A should have a syntax error reported")
def test_yaml_dict_error(self):
in_repo_conf = textwrap.dedent(
"""
- job
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('not a dictionary', A.messages[0],
"A should have a syntax error reported")
def test_yaml_duplicate_key_error(self):
in_repo_conf = textwrap.dedent(
"""
- job:
name: foo
name: bar
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('appears more than once', A.messages[0],
"A should have a syntax error reported")
def test_yaml_key_error(self):
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test2
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('has more than one key', A.messages[0],
"A should have a syntax error reported")
def test_yaml_unknown_error(self):
in_repo_conf = textwrap.dedent(
"""
- foobar:
foo: bar
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('not recognized', A.messages[0],
"A should have a syntax error reported")
def test_untrusted_syntax_error(self):
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test2
foo: error
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('syntax error', A.messages[0],
"A should have a syntax error reported")
def test_trusted_syntax_error(self):
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test2
foo: error
""")
file_dict = {'zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('common-config', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('syntax error', A.messages[0],
"A should have a syntax error reported")
def test_untrusted_yaml_error(self):
in_repo_conf = textwrap.dedent(
"""
- job:
foo: error
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('syntax error', A.messages[0],
"A should have a syntax error reported")
def test_untrusted_shadow_error(self):
in_repo_conf = textwrap.dedent(
"""
- job:
name: common-config-test
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('not permitted to shadow', A.messages[0],
"A should have a syntax error reported")
def test_untrusted_pipeline_error(self):
in_repo_conf = textwrap.dedent(
"""
- pipeline:
name: test
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('Pipelines may not be defined', A.messages[0],
"A should have a syntax error reported")
def test_untrusted_project_error(self):
in_repo_conf = textwrap.dedent(
"""
- project:
name: org/project1
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('the only project definition permitted', A.messages[0],
"A should have a syntax error reported")
def test_untrusted_depends_on_trusted(self):
with open(os.path.join(FIXTURE_DIR,
'config/in-repo/git/',
'common-config/zuul.yaml')) as f:
common_config = f.read()
common_config += textwrap.dedent(
"""
- job:
name: project-test9
""")
file_dict = {'zuul.yaml': common_config}
A = self.fake_gerrit.addFakeChange('common-config', 'master', 'A',
files=file_dict)
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
- project:
name: org/project
check:
jobs:
- project-test9
""")
file_dict = {'zuul.yaml': in_repo_conf}
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B',
files=file_dict)
B.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
B.subject, A.data['id'])
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(B.data['status'], 'NEW')
self.assertEqual(B.reported, 1,
"B should report failure")
self.assertIn('depends on a change to a config project',
B.messages[0],
"A should have a syntax error reported")
def test_duplicate_node_error(self):
in_repo_conf = textwrap.dedent(
"""
- nodeset:
name: duplicate
nodes:
- name: compute
label: foo
- name: compute
label: foo
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('appears multiple times', A.messages[0],
"A should have a syntax error reported")
def test_duplicate_group_error(self):
in_repo_conf = textwrap.dedent(
"""
- nodeset:
name: duplicate
nodes:
- name: compute
label: foo
groups:
- name: group
nodes: compute
- name: group
nodes: compute
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('appears multiple times', A.messages[0],
"A should have a syntax error reported")
def test_secret_not_found_error(self):
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
run: playbooks/project-test1.yaml
secrets: does-not-exist
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('secret "does-not-exist" was not found', A.messages[0],
"A should have a syntax error reported")
def test_nodeset_not_found_error(self):
in_repo_conf = textwrap.dedent(
"""
- job:
name: test
nodeset: does-not-exist
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('nodeset "does-not-exist" was not found', A.messages[0],
"A should have a syntax error reported")
def test_required_project_not_found_error(self):
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
- job:
name: test
required-projects:
- does-not-exist
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('Unknown project does-not-exist', A.messages[0],
"A should have a syntax error reported")
def test_template_not_found_error(self):
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
- project:
name: org/project
templates:
- does-not-exist
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('project template "does-not-exist" was not found',
A.messages[0],
"A should have a syntax error reported")
def test_job_list_in_project_template_not_dict_error(self):
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
- project-template:
name: some-jobs
check:
jobs:
- project-test1:
- required-projects:
org/project2
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('expected str for dictionary value',
A.messages[0], "A should have a syntax error reported")
def test_job_list_in_project_not_dict_error(self):
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
- project:
name: org/project1
check:
jobs:
- project-test1:
- required-projects:
org/project2
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('expected str for dictionary value',
A.messages[0], "A should have a syntax error reported")
def test_project_template(self):
# Tests that a project template is not modified when used, and
# can therefore be used in subsequent reconfigurations.
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
run: playbooks/project-test1.yaml
- project-template:
name: some-jobs
tenant-one-gate:
jobs:
- project-test1:
required-projects:
- org/project1
- project:
name: org/project
templates:
- some-jobs
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'MERGED')
self.fake_gerrit.addEvent(A.getChangeMergedEvent())
self.waitUntilSettled()
in_repo_conf = textwrap.dedent(
"""
- project:
name: org/project1
templates:
- some-jobs
""")
file_dict = {'.zuul.yaml': in_repo_conf}
B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B',
files=file_dict)
B.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(B.data['status'], 'MERGED')
def test_job_remove_add(self):
# Tests that a job can be removed from one repo and added in another.
# First, remove the current config for project1 since it
# references the job we want to remove.
file_dict = {'.zuul.yaml': None}
A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A',
files=file_dict)
A.setMerged()
self.fake_gerrit.addEvent(A.getChangeMergedEvent())
self.waitUntilSettled()
# Then propose a change to delete the job from one repo...
file_dict = {'.zuul.yaml': None}
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B',
files=file_dict)
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
# ...and a second that depends on it that adds it to another repo.
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
run: playbooks/project-test1.yaml
- project:
name: org/project1
check:
jobs:
- project-test1
""")
in_repo_playbook = textwrap.dedent(
"""
- hosts: all
tasks: []
""")
file_dict = {'.zuul.yaml': in_repo_conf,
'playbooks/project-test1.yaml': in_repo_playbook}
C = self.fake_gerrit.addFakeChange('org/project1', 'master', 'C',
files=file_dict,
parent='refs/changes/1/1/1')
C.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
C.subject, B.data['id'])
self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertHistory([
dict(name='project-test1', result='SUCCESS', changes='2,1 3,1'),
], ordered=False)
def test_multi_repo(self):
downstream_repo_conf = textwrap.dedent(
"""
- project:
name: org/project1
tenant-one-gate:
jobs:
- project-test1
- job:
name: project1-test1
parent: project-test1
""")
file_dict = {'.zuul.yaml': downstream_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'MERGED')
self.fake_gerrit.addEvent(A.getChangeMergedEvent())
self.waitUntilSettled()
upstream_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
run: playbooks/project-test1.yaml
- job:
name: project-test2
- project:
name: org/project
tenant-one-gate:
jobs:
- project-test1
""")
file_dict = {'.zuul.yaml': upstream_repo_conf}
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B',
files=file_dict)
B.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(B.data['status'], 'MERGED')
self.fake_gerrit.addEvent(B.getChangeMergedEvent())
self.waitUntilSettled()
tenant = self.sched.abide.tenants.get('tenant-one')
# Ensure the latest change is reflected in the config; if it
# isn't this will raise an exception.
tenant.layout.getJob('project-test2')
def test_pipeline_error(self):
with open(os.path.join(FIXTURE_DIR,
'config/in-repo/git/',
'common-config/zuul.yaml')) as f:
base_common_config = f.read()
in_repo_conf_A = textwrap.dedent(
"""
- pipeline:
name: periodic
foo: error
""")
file_dict = {'zuul.yaml': None,
'zuul.d/main.yaml': base_common_config,
'zuul.d/test1.yaml': in_repo_conf_A}
A = self.fake_gerrit.addFakeChange('common-config', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(A.reported, 1,
"A should report failure")
self.assertIn('syntax error',
A.messages[0],
"A should have an error reported")
def test_change_series_error(self):
with open(os.path.join(FIXTURE_DIR,
'config/in-repo/git/',
'common-config/zuul.yaml')) as f:
base_common_config = f.read()
in_repo_conf_A = textwrap.dedent(
"""
- pipeline:
name: periodic
foo: error
""")
file_dict = {'zuul.yaml': None,
'zuul.d/main.yaml': base_common_config,
'zuul.d/test1.yaml': in_repo_conf_A}
A = self.fake_gerrit.addFakeChange('common-config', 'master', 'A',
files=file_dict)
in_repo_conf_B = textwrap.dedent(
"""
- job:
name: project-test2
foo: error
""")
file_dict = {'zuul.yaml': None,
'zuul.d/main.yaml': base_common_config,
'zuul.d/test1.yaml': in_repo_conf_A,
'zuul.d/test2.yaml': in_repo_conf_B}
B = self.fake_gerrit.addFakeChange('common-config', 'master', 'B',
files=file_dict)
B.setDependsOn(A, 1)
C = self.fake_gerrit.addFakeChange('common-config', 'master', 'C')
C.setDependsOn(B, 1)
self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(C.reported, 1,
"C should report failure")
self.assertIn('Zuul encountered a syntax error while parsing '
'its configuration',
C.messages[0],
"C should have an error reported")
def test_pipeline_debug(self):
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
run: playbooks/project-test1.yaml
- project:
name: org/project
check:
debug: True
jobs:
- project-test1
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1,
"A should report success")
self.assertIn('Debug information:',
A.messages[0], "A should have debug info")
class TestJobContamination(AnsibleZuulTestCase):
config_file = 'zuul-connections-gerrit-and-github.conf'
tenant_config_file = 'config/zuul-job-contamination/main.yaml'
def test_job_contamination_playbooks(self):
conf = textwrap.dedent(
"""
- job:
name: base
post-run:
- playbooks/something-new.yaml
parent: null
vars:
basevar: basejob
""")
file_dict = {'zuul.d/jobs.yaml': conf}
A = self.fake_github.openFakePullRequest(
'org/global-config', 'master', 'A', files=file_dict)
self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
self.waitUntilSettled()
B = self.fake_github.openFakePullRequest('org/project1', 'master', 'A')
self.fake_github.emitEvent(B.getPullRequestOpenedEvent())
self.waitUntilSettled()
statuses_b = self.fake_github.getCommitStatuses(
'org/project1', B.head_sha)
self.assertEqual(len(statuses_b), 1)
# B should not be affected by the A PR
self.assertEqual('success', statuses_b[0]['state'])
def test_job_contamination_vars(self):
conf = textwrap.dedent(
"""
- job:
name: base
parent: null
vars:
basevar: basejob-modified
""")
file_dict = {'zuul.d/jobs.yaml': conf}
A = self.fake_github.openFakePullRequest(
'org/global-config', 'master', 'A', files=file_dict)
self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
self.waitUntilSettled()
B = self.fake_github.openFakePullRequest('org/project1', 'master', 'A')
self.fake_github.emitEvent(B.getPullRequestOpenedEvent())
self.waitUntilSettled()
statuses_b = self.fake_github.getCommitStatuses(
'org/project1', B.head_sha)
self.assertEqual(len(statuses_b), 1)
# B should not be affected by the A PR
self.assertEqual('success', statuses_b[0]['state'])
class TestInRepoJoin(ZuulTestCase):
# In this config, org/project is not a member of any pipelines, so
# that we may test the changes that cause it to join them.
tenant_config_file = 'config/in-repo-join/main.yaml'
def test_dynamic_dependent_pipeline(self):
# Test dynamically adding a project to a
# dependent pipeline for the first time
self.executor_server.hold_jobs_in_build = True
tenant = self.sched.abide.tenants.get('tenant-one')
gate_pipeline = tenant.layout.pipelines['gate']
self.assertEqual(gate_pipeline.queues, [])
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
run: playbooks/project-test1.yaml
- job:
name: project-test2
run: playbooks/project-test2.yaml
- project:
name: org/project
gate:
jobs:
- project-test2
""")
in_repo_playbook = textwrap.dedent(
"""
- hosts: all
tasks: []
""")
file_dict = {'.zuul.yaml': in_repo_conf,
'playbooks/project-test2.yaml': in_repo_playbook}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
items = gate_pipeline.getAllItems()
self.assertEqual(items[0].change.number, '1')
self.assertEqual(items[0].change.patchset, '1')
self.assertTrue(items[0].live)
self.executor_server.hold_jobs_in_build = False
self.executor_server.release()
self.waitUntilSettled()
# Make sure the dynamic queue got cleaned up
self.assertEqual(gate_pipeline.queues, [])
def test_dynamic_dependent_pipeline_failure(self):
# Test that a change behind a failing change adding a project
# to a dependent pipeline is dequeued.
self.executor_server.hold_jobs_in_build = True
in_repo_conf = textwrap.dedent(
"""
- job:
name: project-test1
run: playbooks/project-test1.yaml
- project:
name: org/project
gate:
jobs:
- project-test1
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
self.executor_server.failJob('project-test1', A)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
B.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
self.waitUntilSettled()
self.orderedRelease()
self.waitUntilSettled()
self.assertEqual(A.reported, 2,
"A should report start and failure")
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(B.reported, 1,
"B should report start")
self.assertHistory([
dict(name='project-test1', result='FAILURE', changes='1,1'),
dict(name='project-test1', result='ABORTED', changes='1,1 2,1'),
], ordered=False)
def test_dynamic_dependent_pipeline_absent(self):
# Test that a series of dependent changes don't report merge
# failures to a pipeline they aren't in.
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
B.setDependsOn(A, 1)
A.addApproval('Code-Review', 2)
A.addApproval('Approved', 1)
B.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.reported, 0,
"A should not report")
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(B.reported, 0,
"B should not report")
self.assertEqual(B.data['status'], 'NEW')
self.assertHistory([])
class TestAnsible(AnsibleZuulTestCase):
# A temporary class to hold new tests while others are disabled
tenant_config_file = 'config/ansible/main.yaml'
def test_playbook(self):
# This test runs a bit long and needs extra time.
self.wait_timeout = 120
# Keep the jobdir around so we can inspect contents if an
# assert fails.
self.executor_server.keep_jobdir = True
# Output extra ansible info so we might see errors.
self.executor_server.verbose = True
# Add a site variables file, used by check-vars
path = os.path.join(FIXTURE_DIR, 'config', 'ansible',
'variables.yaml')
self.config.set('executor', 'variables', path)
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
build_timeout = self.getJobFromHistory('timeout')
with self.jobLog(build_timeout):
self.assertEqual(build_timeout.result, 'TIMED_OUT')
post_flag_path = os.path.join(self.test_root, build_timeout.uuid +
'.post.flag')
self.assertTrue(os.path.exists(post_flag_path))
build_post_timeout = self.getJobFromHistory('post-timeout')
with self.jobLog(build_post_timeout):
self.assertEqual(build_post_timeout.result, 'POST_FAILURE')
build_faillocal = self.getJobFromHistory('faillocal')
with self.jobLog(build_faillocal):
self.assertEqual(build_faillocal.result, 'FAILURE')
build_failpost = self.getJobFromHistory('failpost')
with self.jobLog(build_failpost):
self.assertEqual(build_failpost.result, 'POST_FAILURE')
build_check_vars = self.getJobFromHistory('check-vars')
with self.jobLog(build_check_vars):
self.assertEqual(build_check_vars.result, 'SUCCESS')
build_check_hostvars = self.getJobFromHistory('check-hostvars')
with self.jobLog(build_check_hostvars):
self.assertEqual(build_check_hostvars.result, 'SUCCESS')
build_check_secret_names = self.getJobFromHistory('check-secret-names')
with self.jobLog(build_check_secret_names):
self.assertEqual(build_check_secret_names.result, 'SUCCESS')
build_hello = self.getJobFromHistory('hello-world')
with self.jobLog(build_hello):
self.assertEqual(build_hello.result, 'SUCCESS')
build_add_host = self.getJobFromHistory('add-host')
with self.jobLog(build_add_host):
self.assertEqual(build_add_host.result, 'SUCCESS')
build_python27 = self.getJobFromHistory('python27')
with self.jobLog(build_python27):
self.assertEqual(build_python27.result, 'SUCCESS')
flag_path = os.path.join(self.test_root,
build_python27.uuid + '.flag')
self.assertTrue(os.path.exists(flag_path))
copied_path = os.path.join(self.test_root, build_python27.uuid +
'.copied')
self.assertTrue(os.path.exists(copied_path))
failed_path = os.path.join(self.test_root, build_python27.uuid +
'.failed')
self.assertFalse(os.path.exists(failed_path))
pre_flag_path = os.path.join(self.test_root, build_python27.uuid +
'.pre.flag')
self.assertTrue(os.path.exists(pre_flag_path))
post_flag_path = os.path.join(self.test_root, build_python27.uuid +
'.post.flag')
self.assertTrue(os.path.exists(post_flag_path))
bare_role_flag_path = os.path.join(self.test_root,
build_python27.uuid +
'.bare-role.flag')
self.assertTrue(os.path.exists(bare_role_flag_path))
secrets_path = os.path.join(self.test_root,
build_python27.uuid + '.secrets')
with open(secrets_path) as f:
self.assertEqual(f.read(), "test-username test-password")
msg = A.messages[0]
success = "{} https://success.example.com/zuul-logs/{}"
fail = "{} https://failure.example.com/zuul-logs/{}"
self.assertIn(success.format("python27", build_python27.uuid), msg)
self.assertIn(fail.format("faillocal", build_faillocal.uuid), msg)
self.assertIn(success.format("check-vars",
build_check_vars.uuid), msg)
self.assertIn(success.format("hello-world", build_hello.uuid), msg)
self.assertIn(fail.format("timeout", build_timeout.uuid), msg)
self.assertIn(fail.format("failpost", build_failpost.uuid), msg)
def test_repo_ansible(self):
A = self.fake_gerrit.addFakeChange('org/ansible', 'master', 'A')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(A.reported, 1,
"A should report success")
self.assertHistory([
dict(name='hello-ansible', result='SUCCESS', changes='1,1'),
])
def _add_job(self, job_name):
conf = textwrap.dedent(
"""
- job:
name: %s
run: playbooks/%s.yaml
- project:
name: org/plugin-project
check:
jobs:
- %s
""" % (job_name, job_name, job_name))
file_dict = {'.zuul.yaml': conf}
A = self.fake_gerrit.addFakeChange('org/plugin-project', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
def test_plugins(self):
# Keep the jobdir around so we can inspect contents if an
# assert fails.
self.