# 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 gc
|
|
import json
|
|
import textwrap
|
|
|
|
import os
|
|
import shutil
|
|
import socket
|
|
import time
|
|
from unittest import mock
|
|
from unittest import skip
|
|
from kazoo.exceptions import NoNodeError
|
|
|
|
import git
|
|
import testtools
|
|
from zuul.scheduler import Scheduler
|
|
|
|
import zuul.change_matcher
|
|
from zuul.driver.gerrit import gerritreporter
|
|
import zuul.scheduler
|
|
import zuul.rpcclient
|
|
import zuul.model
|
|
|
|
from tests.base import (
|
|
SSLZuulTestCase,
|
|
ZuulTestCase,
|
|
repack_repo,
|
|
simple_layout,
|
|
iterate_timeout,
|
|
)
|
|
|
|
|
|
class TestSchedulerSSL(SSLZuulTestCase):
|
|
tenant_config_file = 'config/single-tenant/main.yaml'
|
|
|
|
def test_jobs_executed(self):
|
|
"Test that jobs are executed and a change is merged"
|
|
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-merge').result,
|
|
'SUCCESS')
|
|
self.assertEqual(self.getJobFromHistory('project-test1').result,
|
|
'SUCCESS')
|
|
self.assertEqual(self.getJobFromHistory('project-test2').result,
|
|
'SUCCESS')
|
|
self.assertEqual(A.data['status'], 'MERGED')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertEqual(self.getJobFromHistory('project-test1').node,
|
|
'label1')
|
|
self.assertEqual(self.getJobFromHistory('project-test2').node,
|
|
'label1')
|
|
|
|
|
|
class TestSchedulerZone(ZuulTestCase):
|
|
tenant_config_file = 'config/single-tenant/main.yaml'
|
|
|
|
def setUp(self):
|
|
super(TestSchedulerZone, self).setUp()
|
|
self.fake_nodepool.attributes = {'executor-zone': 'test-provider.vpn'}
|
|
|
|
def setup_config(self, config_file: str):
|
|
config = super(TestSchedulerZone, self).setup_config(config_file)
|
|
config.set('executor', 'zone', 'test-provider.vpn')
|
|
return config
|
|
|
|
def test_jobs_executed(self):
|
|
"Test that jobs are executed and a change is merged per zone"
|
|
self.gearman_server.hold_jobs_in_queue = True
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
A.addApproval('Code-Review', 2)
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
|
|
self.waitUntilSettled()
|
|
|
|
queue = self.gearman_server.getQueue()
|
|
self.assertEqual(len(self.builds), 0)
|
|
self.assertEqual(len(queue), 1)
|
|
self.assertEqual(b'executor:execute:test-provider.vpn', queue[0].name)
|
|
|
|
self.gearman_server.hold_jobs_in_queue = False
|
|
self.gearman_server.release()
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(self.getJobFromHistory('project-merge').result,
|
|
'SUCCESS')
|
|
self.assertEqual(self.getJobFromHistory('project-test1').result,
|
|
'SUCCESS')
|
|
self.assertEqual(self.getJobFromHistory('project-test2').result,
|
|
'SUCCESS')
|
|
self.assertEqual(A.data['status'], 'MERGED')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertEqual(self.getJobFromHistory('project-test1').node,
|
|
'label1')
|
|
self.assertEqual(self.getJobFromHistory('project-test2').node,
|
|
'label1')
|
|
|
|
|
|
class TestAuthorizeViaRPC(ZuulTestCase):
|
|
tenant_config_file = 'config/authorization/single-tenant/main.yaml'
|
|
|
|
def test_authorize_via_rpc(self):
|
|
client = zuul.rpcclient.RPCClient('127.0.0.1',
|
|
self.gearman_server.port)
|
|
self.addCleanup(client.shutdown)
|
|
claims = {'__zuul_uid_claim': 'venkman'}
|
|
authorized = client.submitJob('zuul:authorize_user',
|
|
{'tenant': 'tenant-one',
|
|
'claims': claims}).data[0]
|
|
self.assertTrue(json.loads(authorized))
|
|
claims = {'sub': 'gozer'}
|
|
authorized = client.submitJob('zuul:authorize_user',
|
|
{'tenant': 'tenant-one',
|
|
'claims': claims}).data[0]
|
|
self.assertTrue(not json.loads(authorized))
|
|
claims = {'sub': 'stantz',
|
|
'iss': 'columbia.edu'}
|
|
authorized = client.submitJob('zuul:authorize_user',
|
|
{'tenant': 'tenant-one',
|
|
'claims': claims}).data[0]
|
|
self.assertTrue(json.loads(authorized))
|
|
claims = {'sub': 'slimer',
|
|
'groups': ['ghostbusters', 'ectoplasms']}
|
|
authorized = client.submitJob('zuul:authorize_user',
|
|
{'tenant': 'tenant-one',
|
|
'claims': claims}).data[0]
|
|
self.assertTrue(json.loads(authorized))
|
|
|
|
|
|
class TestAuthorizeWithTemplatingViaRPC(ZuulTestCase):
|
|
tenant_config_file = 'config/authorization/rules-templating/main.yaml'
|
|
|
|
def test_authorize_via_rpc(self):
|
|
client = zuul.rpcclient.RPCClient('127.0.0.1',
|
|
self.gearman_server.port)
|
|
self.addCleanup(client.shutdown)
|
|
tenants = ['tenant-zero', 'tenant-one', 'tenant-two']
|
|
for t_claim in tenants:
|
|
claims = {'groups': [t_claim, ]}
|
|
for tenant in tenants:
|
|
authorized = client.submitJob('zuul:authorize_user',
|
|
{'tenant': tenant,
|
|
'claims': claims}).data[0]
|
|
if t_claim == tenant:
|
|
self.assertTrue(
|
|
json.loads(authorized),
|
|
"Failed for t_claim: %s, tenant: %s" % (t_claim,
|
|
tenant))
|
|
else:
|
|
self.assertTrue(
|
|
not json.loads(authorized),
|
|
"Failed for t_claim: %s, tenant: %s" % (t_claim,
|
|
tenant))
|
|
|
|
|
|
class TestSchedulerAutoholdHoldExpiration(ZuulTestCase):
|
|
'''
|
|
This class of tests validates the autohold node expiration values
|
|
are set correctly via zuul config or from a custom value.
|
|
'''
|
|
config_file = 'zuul-hold-expiration.conf'
|
|
tenant_config_file = 'config/single-tenant/main.yaml'
|
|
|
|
@simple_layout('layouts/autohold.yaml')
|
|
def test_autohold_max_hold_default(self):
|
|
'''
|
|
Test that the hold request node expiration will default to the
|
|
value specified in the configuration file.
|
|
'''
|
|
client = zuul.rpcclient.RPCClient('127.0.0.1',
|
|
self.gearman_server.port)
|
|
self.addCleanup(client.shutdown)
|
|
|
|
# Add a autohold with no hold expiration.
|
|
r = client.autohold('tenant-one', 'org/project', 'project-test2',
|
|
"", "", "reason text", 1)
|
|
self.assertTrue(r)
|
|
|
|
# There should be a record in ZooKeeper
|
|
request_list = self.scheds.first.sched.zk.getHoldRequests()
|
|
self.assertEqual(1, len(request_list))
|
|
request = self.scheds.first.sched.zk.getHoldRequest(request_list[0])
|
|
self.assertIsNotNone(request)
|
|
self.assertEqual('tenant-one', request.tenant)
|
|
self.assertEqual('review.example.com/org/project', request.project)
|
|
self.assertEqual('project-test2', request.job)
|
|
self.assertEqual('reason text', request.reason)
|
|
self.assertEqual(1, request.max_count)
|
|
self.assertEqual(0, request.current_count)
|
|
self.assertEqual([], request.nodes)
|
|
# This should be the default value from the zuul config file.
|
|
self.assertEqual(1800, request.node_expiration)
|
|
|
|
@simple_layout('layouts/autohold.yaml')
|
|
def test_autohold_max_hold_custom(self):
|
|
'''
|
|
Test that the hold request node expiration will be set to the custom
|
|
value specified in the request.
|
|
'''
|
|
client = zuul.rpcclient.RPCClient('127.0.0.1',
|
|
self.gearman_server.port)
|
|
self.addCleanup(client.shutdown)
|
|
|
|
# Add a autohold with a custom hold expiration.
|
|
r = client.autohold('tenant-one', 'org/project', 'project-test2',
|
|
"", "", "reason text", 1, 500)
|
|
self.assertTrue(r)
|
|
|
|
# There should be a record in ZooKeeper
|
|
request_list = self.scheds.first.sched.zk.getHoldRequests()
|
|
self.assertEqual(1, len(request_list))
|
|
request = self.scheds.first.sched.zk.getHoldRequest(request_list[0])
|
|
self.assertIsNotNone(request)
|
|
self.assertEqual('tenant-one', request.tenant)
|
|
self.assertEqual('review.example.com/org/project', request.project)
|
|
self.assertEqual('project-test2', request.job)
|
|
self.assertEqual('reason text', request.reason)
|
|
self.assertEqual(1, request.max_count)
|
|
self.assertEqual(0, request.current_count)
|
|
self.assertEqual([], request.nodes)
|
|
# This should be the value from the user request.
|
|
self.assertEqual(500, request.node_expiration)
|
|
|
|
@simple_layout('layouts/autohold.yaml')
|
|
def test_autohold_max_hold_custom_invalid(self):
|
|
'''
|
|
Test that if the custom hold request node expiration is higher than our
|
|
configured max, it will be lowered to the max.
|
|
'''
|
|
client = zuul.rpcclient.RPCClient('127.0.0.1',
|
|
self.gearman_server.port)
|
|
self.addCleanup(client.shutdown)
|
|
|
|
# Add a autohold with a custom hold expiration that is higher than our
|
|
# configured max.
|
|
r = client.autohold('tenant-one', 'org/project', 'project-test2',
|
|
"", "", "reason text", 1, 10000)
|
|
self.assertTrue(r)
|
|
|
|
# There should be a record in ZooKeeper
|
|
request_list = self.scheds.first.sched.zk.getHoldRequests()
|
|
self.assertEqual(1, len(request_list))
|
|
request = self.scheds.first.sched.zk.getHoldRequest(request_list[0])
|
|
self.assertIsNotNone(request)
|
|
self.assertEqual('tenant-one', request.tenant)
|
|
self.assertEqual('review.example.com/org/project', request.project)
|
|
self.assertEqual('project-test2', request.job)
|
|
self.assertEqual('reason text', request.reason)
|
|
self.assertEqual(1, request.max_count)
|
|
self.assertEqual(0, request.current_count)
|
|
self.assertEqual([], request.nodes)
|
|
# This should be the max value from the zuul config file.
|
|
self.assertEqual(3600, request.node_expiration)
|
|
|
|
|
|
class TestScheduler(ZuulTestCase):
|
|
tenant_config_file = 'config/single-tenant/main.yaml'
|
|
|
|
def test_jobs_executed(self):
|
|
"Test that jobs are executed and a change is merged"
|
|
|
|
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-merge').result,
|
|
'SUCCESS')
|
|
self.assertEqual(self.getJobFromHistory('project-test1').result,
|
|
'SUCCESS')
|
|
self.assertEqual(self.getJobFromHistory('project-test2').result,
|
|
'SUCCESS')
|
|
self.assertEqual(A.data['status'], 'MERGED')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertEqual(self.getJobFromHistory('project-test1').node,
|
|
'label1')
|
|
self.assertEqual(self.getJobFromHistory('project-test2').node,
|
|
'label1')
|
|
|
|
# TODOv3(jeblair): we may want to report stats by tenant (also?).
|
|
# Per-driver
|
|
self.assertReportedStat('zuul.event.gerrit.comment-added', value='1',
|
|
kind='c')
|
|
# Per-driver per-connection
|
|
self.assertReportedStat('zuul.event.gerrit.gerrit.comment-added',
|
|
value='1', kind='c')
|
|
self.assertReportedStat(
|
|
'zuul.tenant.tenant-one.pipeline.gate.current_changes',
|
|
value='1', kind='g')
|
|
self.assertReportedStat(
|
|
'zuul.tenant.tenant-one.pipeline.gate.project.review_example_com.'
|
|
'org_project.master.job.project-merge.SUCCESS', kind='ms')
|
|
self.assertReportedStat(
|
|
'zuul.tenant.tenant-one.pipeline.gate.project.review_example_com.'
|
|
'org_project.master.job.project-merge.SUCCESS', value='1',
|
|
kind='c')
|
|
self.assertReportedStat(
|
|
'zuul.tenant.tenant-one.pipeline.gate.resident_time', kind='ms')
|
|
self.assertReportedStat(
|
|
'zuul.tenant.tenant-one.pipeline.gate.total_changes', value='1',
|
|
kind='c')
|
|
self.assertReportedStat(
|
|
'zuul.tenant.tenant-one.pipeline.gate.project.review_example_com.'
|
|
'org_project.master.resident_time', kind='ms')
|
|
self.assertReportedStat(
|
|
'zuul.tenant.tenant-one.pipeline.gate.project.review_example_com.'
|
|
'org_project.master.total_changes', value='1', kind='c')
|
|
exec_key = 'zuul.executor.%s' % self.executor_server.hostname.replace(
|
|
'.', '_')
|
|
self.assertReportedStat(exec_key + '.builds', value='1', kind='c')
|
|
self.assertReportedStat(exec_key + '.starting_builds', kind='g')
|
|
self.assertReportedStat(exec_key + '.starting_builds', kind='ms')
|
|
self.assertReportedStat(
|
|
'zuul.nodepool.requests.requested.total', value='1', kind='c')
|
|
self.assertReportedStat(
|
|
'zuul.nodepool.requests.requested.label.label1',
|
|
value='1', kind='c')
|
|
self.assertReportedStat(
|
|
'zuul.nodepool.requests.fulfilled.label.label1',
|
|
value='1', kind='c')
|
|
self.assertReportedStat(
|
|
'zuul.nodepool.requests.requested.size.1', value='1', kind='c')
|
|
self.assertReportedStat(
|
|
'zuul.nodepool.requests.fulfilled.size.1', value='1', kind='c')
|
|
self.assertReportedStat(
|
|
'zuul.nodepool.current_requests', value='1', kind='g')
|
|
self.assertReportedStat(
|
|
'zuul.executors.online', value='1', kind='g')
|
|
self.assertReportedStat(
|
|
'zuul.executors.accepting', value='1', kind='g')
|
|
self.assertReportedStat(
|
|
'zuul.mergers.online', value='1', kind='g')
|
|
|
|
for build in self.history:
|
|
self.assertTrue(build.parameters['zuul']['voting'])
|
|
|
|
def test_initial_pipeline_gauges(self):
|
|
"Test that each pipeline reported its length on start"
|
|
self.assertReportedStat('zuul.tenant.tenant-one.pipeline.gate.'
|
|
'current_changes',
|
|
value='0', kind='g')
|
|
self.assertReportedStat('zuul.tenant.tenant-one.pipeline.check.'
|
|
'current_changes',
|
|
value='0', kind='g')
|
|
|
|
def test_job_branch(self):
|
|
"Test the correct variant of a job runs on a branch"
|
|
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')
|
|
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(self.getJobFromHistory('project-test2').result,
|
|
'SUCCESS')
|
|
self.assertEqual(A.data['status'], 'MERGED')
|
|
self.assertEqual(A.reported, 2,
|
|
"A should report start and success")
|
|
self.assertIn('gate', A.messages[1],
|
|
"A should transit gate")
|
|
self.assertEqual(self.getJobFromHistory('project-test1').node,
|
|
'label2')
|
|
|
|
@simple_layout('layouts/branch-deletion.yaml')
|
|
def test_branch_deletion(self):
|
|
"Test the correct variant of a job runs on a branch"
|
|
self._startMerger()
|
|
merger_gear = self.executor_server.merger_gearworker.gearman
|
|
for f in list(merger_gear.functions.keys()):
|
|
f = f.decode('utf8')
|
|
if f.startswith('merger:'):
|
|
merger_gear.unRegisterFunction(f)
|
|
|
|
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')
|
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
|
|
self.waitUntilSettled()
|
|
self.assertEqual(self.getJobFromHistory('project-test2').result,
|
|
'SUCCESS')
|
|
|
|
self.delete_branch('org/project', 'stable')
|
|
path = os.path.join(self.executor_src_root, 'review.example.com')
|
|
shutil.rmtree(path)
|
|
|
|
self.executor_server.hold_jobs_in_build = True
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
|
|
self.waitUntilSettled()
|
|
build = self.builds[0]
|
|
|
|
# Make sure there is no stable branch in the checked out git repo.
|
|
pname = 'review.example.com/org/project'
|
|
work = build.getWorkspaceRepos([pname])
|
|
work = work[pname]
|
|
heads = set([str(x) for x in work.heads])
|
|
self.assertEqual(heads, set(['master']))
|
|
self.executor_server.hold_jobs_in_build = False
|
|
build.release()
|
|
self.waitUntilSettled()
|
|
self.assertEqual(self.getJobFromHistory('project-test1').result,
|
|
'SUCCESS')
|
|
|
|
def test_parallel_changes(self):
|
|
"Test that changes are tested in parallel and merged in series"
|
|
|
|
self.executor_server.hold_jobs_in_build = True
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
|
|
C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
|
|
A.addApproval('Code-Review', 2)
|
|
B.addApproval('Code-Review', 2)
|
|
C.addApproval('Code-Review', 2)
|
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
|
|
|
|
self.waitUntilSettled()
|
|
self.assertEqual(len(self.builds), 1)
|
|
self.assertEqual(self.builds[0].name, 'project-merge')
|
|
self.assertTrue(self.builds[0].hasChanges(A))
|
|
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.assertEqual(len(self.builds), 3)
|
|
self.assertEqual(self.builds[0].name, 'project-test1')
|
|
self.assertTrue(self.builds[0].hasChanges(A))
|
|
self.assertEqual(self.builds[1].name, 'project-test2')
|
|
self.assertTrue(self.builds[1].hasChanges(A))
|
|
self.assertEqual(self.builds[2].name, 'project-merge')
|
|
self.assertTrue(self.builds[2].hasChanges(A, B))
|
|
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.assertEqual(len(self.builds), 5)
|
|
self.assertEqual(self.builds[0].name, 'project-test1')
|
|
self.assertTrue(self.builds[0].hasChanges(A))
|
|
self.assertEqual(self.builds[1].name, 'project-test2')
|
|
self.assertTrue(self.builds[1].hasChanges(A))
|
|
|
|
self.assertEqual(self.builds[2].name, 'project-test1')
|
|
self.assertTrue(self.builds[2].hasChanges(A, B))
|
|
self.assertEqual(self.builds[3].name, 'project-test2')
|
|
self.assertTrue(self.builds[3].hasChanges(A, B))
|
|
|
|
self.assertEqual(self.builds[4].name, 'project-merge')
|
|
self.assertTrue(self.builds[4].hasChanges(A, B, C))
|
|
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.assertEqual(len(self.builds), 6)
|
|
self.assertEqual(self.builds[0].name, 'project-test1')
|
|
self.assertTrue(self.builds[0].hasChanges(A))
|
|
self.assertEqual(self.builds[1].name, 'project-test2')
|
|
self.assertTrue(self.builds[1].hasChanges(A))
|
|
|
|
self.assertEqual(self.builds[2].name, 'project-test1')
|
|
self.assertTrue(self.builds[2].hasChanges(A, B))
|
|
self.assertEqual(self.builds[3].name, 'project-test2')
|
|
self.assertTrue(self.builds[3].hasChanges(A, B))
|
|
|
|
self.assertEqual(self.builds[4].name, 'project-test1')
|
|
self.assertTrue(self.builds[4].hasChanges(A, B, C))
|
|
self.assertEqual(self.builds[5].name, 'project-test2')
|
|
self.assertTrue(self.builds[5].hasChanges(A, B, C))
|
|
|
|
self.executor_server.hold_jobs_in_build = False
|
|
self.executor_server.release()
|
|
self.waitUntilSettled()
|
|
self.assertEqual(len(self.builds), 0)
|
|
|
|
self.assertEqual(len(self.history), 9)
|
|
self.assertEqual(A.data['status'], 'MERGED')
|
|
self.assertEqual(B.data['status'], 'MERGED')
|
|
self.assertEqual(C.data['status'], 'MERGED')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertEqual(B.reported, 2)
|
|
self.assertEqual(C.reported, 2)
|
|
|
|
def test_failed_changes(self):
|
|
"Test that a change behind a failed change is retested"
|
|
self.executor_server.hold_jobs_in_build = True
|
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
|
|
A.addApproval('Code-Review', 2)
|
|
B.addApproval('Code-Review', 2)
|
|
|
|
self.executor_server.failJob('project-test1', A)
|
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
|
|
self.waitUntilSettled()
|
|
self.assertBuilds([dict(name='project-merge', changes='1,1')])
|
|
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
# A/project-merge is complete
|
|
self.assertBuilds([
|
|
dict(name='project-test1', changes='1,1'),
|
|
dict(name='project-test2', changes='1,1'),
|
|
dict(name='project-merge', changes='1,1 2,1'),
|
|
])
|
|
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
# A/project-merge is complete
|
|
# B/project-merge is complete
|
|
self.assertBuilds([
|
|
dict(name='project-test1', changes='1,1'),
|
|
dict(name='project-test2', changes='1,1'),
|
|
dict(name='project-test1', changes='1,1 2,1'),
|
|
dict(name='project-test2', changes='1,1 2,1'),
|
|
])
|
|
|
|
# Release project-test1 for A which will fail. This will
|
|
# abort both running B jobs and reexecute project-merge for B.
|
|
self.builds[0].release()
|
|
self.waitUntilSettled()
|
|
|
|
self.orderedRelease()
|
|
self.assertHistory([
|
|
dict(name='project-merge', result='SUCCESS', changes='1,1'),
|
|
dict(name='project-merge', result='SUCCESS', changes='1,1 2,1'),
|
|
dict(name='project-test1', result='FAILURE', changes='1,1'),
|
|
dict(name='project-test1', result='ABORTED', changes='1,1 2,1'),
|
|
dict(name='project-test2', result='ABORTED', changes='1,1 2,1'),
|
|
dict(name='project-test2', result='SUCCESS', changes='1,1'),
|
|
dict(name='project-merge', result='SUCCESS', changes='2,1'),
|
|
dict(name='project-test1', result='SUCCESS', changes='2,1'),
|
|
dict(name='project-test2', result='SUCCESS', changes='2,1'),
|
|
], ordered=False)
|
|
|
|
self.assertEqual(A.data['status'], 'NEW')
|
|
self.assertEqual(B.data['status'], 'MERGED')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertEqual(B.reported, 2)
|
|
|
|
def test_independent_queues(self):
|
|
"Test that changes end up in the right queues"
|
|
|
|
self.executor_server.hold_jobs_in_build = True
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
|
|
C = self.fake_gerrit.addFakeChange('org/project2', 'master', 'C')
|
|
A.addApproval('Code-Review', 2)
|
|
B.addApproval('Code-Review', 2)
|
|
C.addApproval('Code-Review', 2)
|
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
|
|
self.waitUntilSettled()
|
|
|
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
|
|
self.waitUntilSettled()
|
|
|
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
|
|
self.waitUntilSettled()
|
|
|
|
# There should be one merge job at the head of each queue running
|
|
self.assertBuilds([
|
|
dict(name='project-merge', changes='1,1'),
|
|
dict(name='project-merge', changes='2,1'),
|
|
])
|
|
|
|
# Release the current merge builds
|
|
self.builds[0].release()
|
|
self.waitUntilSettled()
|
|
self.builds[0].release()
|
|
self.waitUntilSettled()
|
|
# Release the merge job for project2 which is behind project1
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
|
|
# All the test builds should be running:
|
|
self.assertBuilds([
|
|
dict(name='project-test1', changes='1,1'),
|
|
dict(name='project-test2', changes='1,1'),
|
|
dict(name='project-test1', changes='2,1'),
|
|
dict(name='project-test2', changes='2,1'),
|
|
dict(name='project1-project2-integration', changes='2,1'),
|
|
dict(name='project-test1', changes='2,1 3,1'),
|
|
dict(name='project-test2', changes='2,1 3,1'),
|
|
dict(name='project1-project2-integration', changes='2,1 3,1'),
|
|
])
|
|
|
|
self.orderedRelease()
|
|
self.assertHistory([
|
|
dict(name='project-merge', result='SUCCESS', changes='1,1'),
|
|
dict(name='project-merge', result='SUCCESS', changes='2,1'),
|
|
dict(name='project-merge', result='SUCCESS', changes='2,1 3,1'),
|
|
dict(name='project-test1', result='SUCCESS', changes='1,1'),
|
|
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='2,1'),
|
|
dict(
|
|
name='project1-project2-integration',
|
|
result='SUCCESS',
|
|
changes='2,1'),
|
|
dict(name='project-test1', result='SUCCESS', changes='2,1 3,1'),
|
|
dict(name='project-test2', result='SUCCESS', changes='2,1 3,1'),
|
|
dict(name='project1-project2-integration',
|
|
result='SUCCESS',
|
|
changes='2,1 3,1'),
|
|
])
|
|
|
|
self.assertEqual(A.data['status'], 'MERGED')
|
|
self.assertEqual(B.data['status'], 'MERGED')
|
|
self.assertEqual(C.data['status'], 'MERGED')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertEqual(B.reported, 2)
|
|
self.assertEqual(C.reported, 2)
|
|
|
|
def test_failed_change_at_head(self):
|
|
"Test that if a change at the head fails, jobs behind it are canceled"
|
|
|
|
self.executor_server.hold_jobs_in_build = True
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
|
|
C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
|
|
A.addApproval('Code-Review', 2)
|
|
B.addApproval('Code-Review', 2)
|
|
C.addApproval('Code-Review', 2)
|
|
|
|
self.executor_server.failJob('project-test1', A)
|
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
|
|
|
|
self.waitUntilSettled()
|
|
|
|
self.assertBuilds([
|
|
dict(name='project-merge', changes='1,1'),
|
|
])
|
|
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
|
|
self.assertBuilds([
|
|
dict(name='project-test1', changes='1,1'),
|
|
dict(name='project-test2', changes='1,1'),
|
|
dict(name='project-test1', changes='1,1 2,1'),
|
|
dict(name='project-test2', changes='1,1 2,1'),
|
|
dict(name='project-test1', changes='1,1 2,1 3,1'),
|
|
dict(name='project-test2', changes='1,1 2,1 3,1'),
|
|
])
|
|
|
|
self.release(self.builds[0])
|
|
self.waitUntilSettled()
|
|
|
|
# project-test2, project-merge for B
|
|
self.assertBuilds([
|
|
dict(name='project-test2', changes='1,1'),
|
|
dict(name='project-merge', changes='2,1'),
|
|
])
|
|
# Unordered history comparison because the aborts can finish
|
|
# in any order.
|
|
self.assertHistory([
|
|
dict(name='project-merge', result='SUCCESS',
|
|
changes='1,1'),
|
|
dict(name='project-merge', result='SUCCESS',
|
|
changes='1,1 2,1'),
|
|
dict(name='project-merge', result='SUCCESS',
|
|
changes='1,1 2,1 3,1'),
|
|
dict(name='project-test1', result='FAILURE',
|
|
changes='1,1'),
|
|
dict(name='project-test1', result='ABORTED',
|
|
changes='1,1 2,1'),
|
|
dict(name='project-test2', result='ABORTED',
|
|
changes='1,1 2,1'),
|
|
dict(name='project-test1', result='ABORTED',
|
|
changes='1,1 2,1 3,1'),
|
|
dict(name='project-test2', result='ABORTED',
|
|
changes='1,1 2,1 3,1'),
|
|
], ordered=False)
|
|
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.orderedRelease()
|
|
|
|
self.assertBuilds([])
|
|
self.assertHistory([
|
|
dict(name='project-merge', result='SUCCESS',
|
|
changes='1,1'),
|
|
dict(name='project-merge', result='SUCCESS',
|
|
changes='1,1 2,1'),
|
|
dict(name='project-merge', result='SUCCESS',
|
|
changes='1,1 2,1 3,1'),
|
|
dict(name='project-test1', result='FAILURE',
|
|
changes='1,1'),
|
|
dict(name='project-test1', result='ABORTED',
|
|
changes='1,1 2,1'),
|
|
dict(name='project-test2', result='ABORTED',
|
|
changes='1,1 2,1'),
|
|
dict(name='project-test1', result='ABORTED',
|
|
changes='1,1 2,1 3,1'),
|
|
dict(name='project-test2', result='ABORTED',
|
|
changes='1,1 2,1 3,1'),
|
|
dict(name='project-merge', result='SUCCESS',
|
|
changes='2,1'),
|
|
dict(name='project-merge', result='SUCCESS',
|
|
changes='2,1 3,1'),
|
|
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='2,1'),
|
|
dict(name='project-test1', result='SUCCESS',
|
|
changes='2,1 3,1'),
|
|
dict(name='project-test2', result='SUCCESS',
|
|
changes='2,1 3,1'),
|
|
], ordered=False)
|
|
|
|
self.assertEqual(A.data['status'], 'NEW')
|
|
self.assertEqual(B.data['status'], 'MERGED')
|
|
self.assertEqual(C.data['status'], 'MERGED')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertEqual(B.reported, 2)
|
|
self.assertEqual(C.reported, 2)
|
|
|
|
def test_failed_change_in_middle(self):
|
|
"Test a failed change in the middle of the queue"
|
|
|
|
self.executor_server.hold_jobs_in_build = True
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
|
|
C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
|
|
A.addApproval('Code-Review', 2)
|
|
B.addApproval('Code-Review', 2)
|
|
C.addApproval('Code-Review', 2)
|
|
|
|
self.executor_server.failJob('project-test1', B)
|
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
|
|
|
|
self.waitUntilSettled()
|
|
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(len(self.builds), 6)
|
|
self.assertEqual(self.builds[0].name, 'project-test1')
|
|
self.assertEqual(self.builds[1].name, 'project-test2')
|
|
self.assertEqual(self.builds[2].name, 'project-test1')
|
|
self.assertEqual(self.builds[3].name, 'project-test2')
|
|
self.assertEqual(self.builds[4].name, 'project-test1')
|
|
self.assertEqual(self.builds[5].name, 'project-test2')
|
|
|
|
self.release(self.builds[2])
|
|
self.waitUntilSettled()
|
|
|
|
# project-test1 and project-test2 for A
|
|
# project-test2 for B
|
|
# project-merge for C (without B)
|
|
self.assertEqual(len(self.builds), 4)
|
|
self.assertEqual(self.countJobResults(self.history, 'ABORTED'), 2)
|
|
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
|
|
# project-test1 and project-test2 for A
|
|
# project-test2 for B
|
|
# project-test1 and project-test2 for C
|
|
self.assertEqual(len(self.builds), 5)
|
|
|
|
tenant = self.scheds.first.sched.abide.tenants.get('tenant-one')
|
|
items = tenant.layout.pipelines['gate'].getAllItems()
|
|
builds = items[0].current_build_set.getBuilds()
|
|
self.assertEqual(self.countJobResults(builds, 'SUCCESS'), 1)
|
|
self.assertEqual(self.countJobResults(builds, None), 2)
|
|
builds = items[1].current_build_set.getBuilds()
|
|
self.assertEqual(self.countJobResults(builds, 'SUCCESS'), 1)
|
|
self.assertEqual(self.countJobResults(builds, 'FAILURE'), 1)
|
|
self.assertEqual(self.countJobResults(builds, None), 1)
|
|
builds = items[2].current_build_set.getBuilds()
|
|
self.assertEqual(self.countJobResults(builds, 'SUCCESS'), 1)
|
|
self.assertEqual(self.countJobResults(builds, None), 2)
|
|
|
|
self.executor_server.hold_jobs_in_build = False
|
|
self.executor_server.release()
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(len(self.builds), 0)
|
|
self.assertEqual(len(self.history), 12)
|
|
self.assertEqual(A.data['status'], 'MERGED')
|
|
self.assertEqual(B.data['status'], 'NEW')
|
|
self.assertEqual(C.data['status'], 'MERGED')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertEqual(B.reported, 2)
|
|
self.assertEqual(C.reported, 2)
|
|
|
|
def test_failed_change_at_head_with_queue(self):
|
|
"Test that if a change at the head fails, queued jobs are canceled"
|
|
|
|
self.gearman_server.hold_jobs_in_queue = True
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
|
|
C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
|
|
A.addApproval('Code-Review', 2)
|
|
B.addApproval('Code-Review', 2)
|
|
C.addApproval('Code-Review', 2)
|
|
|
|
self.executor_server.failJob('project-test1', A)
|
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
|
|
|
|
self.waitUntilSettled()
|
|
queue = self.gearman_server.getQueue()
|
|
self.assertEqual(len(self.builds), 0)
|
|
self.assertEqual(len(queue), 1)
|
|
self.assertEqual(queue[0].name, b'executor:execute')
|
|
job_args = json.loads(queue[0].arguments.decode('utf8'))
|
|
self.assertEqual(job_args['job'], 'project-merge')
|
|
self.assertEqual(job_args['items'][0]['number'], '%d' % A.number)
|
|
|
|
self.gearman_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.gearman_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.gearman_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
queue = self.gearman_server.getQueue()
|
|
|
|
self.assertEqual(len(self.builds), 0)
|
|
self.assertEqual(len(queue), 6)
|
|
|
|
self.assertEqual(
|
|
json.loads(queue[0].arguments.decode('utf8'))['job'],
|
|
'project-test1')
|
|
self.assertEqual(
|
|
json.loads(queue[1].arguments.decode('utf8'))['job'],
|
|
'project-test2')
|
|
self.assertEqual(
|
|
json.loads(queue[2].arguments.decode('utf8'))['job'],
|
|
'project-test1')
|
|
self.assertEqual(
|
|
json.loads(queue[3].arguments.decode('utf8'))['job'],
|
|
'project-test2')
|
|
self.assertEqual(
|
|
json.loads(queue[4].arguments.decode('utf8'))['job'],
|
|
'project-test1')
|
|
self.assertEqual(
|
|
json.loads(queue[5].arguments.decode('utf8'))['job'],
|
|
'project-test2')
|
|
|
|
self.release(queue[0])
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(len(self.builds), 0)
|
|
queue = self.gearman_server.getQueue()
|
|
self.assertEqual(len(queue), 2) # project-test2, project-merge for B
|
|
self.assertEqual(self.countJobResults(self.history, 'ABORTED'), 0)
|
|
|
|
self.gearman_server.hold_jobs_in_queue = False
|
|
self.gearman_server.release()
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(len(self.builds), 0)
|
|
self.assertEqual(len(self.history), 11)
|
|
self.assertEqual(A.data['status'], 'NEW')
|
|
self.assertEqual(B.data['status'], 'MERGED')
|
|
self.assertEqual(C.data['status'], 'MERGED')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertEqual(B.reported, 2)
|
|
self.assertEqual(C.reported, 2)
|
|
|
|
def _test_time_database(self, iteration):
|
|
self.executor_server.hold_jobs_in_build = True
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
A.addApproval('Code-Review', 2)
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
|
|
self.waitUntilSettled()
|
|
time.sleep(2)
|
|
|
|
data = json.loads(self.scheds.first.sched
|
|
.formatStatusJSON('tenant-one'))
|
|
found_job = None
|
|
for pipeline in data['pipelines']:
|
|
if pipeline['name'] != 'gate':
|
|
continue
|
|
for queue in pipeline['change_queues']:
|
|
for head in queue['heads']:
|
|
for item in head:
|
|
for job in item['jobs']:
|
|
if job['name'] == 'project-merge':
|
|
found_job = job
|
|
break
|
|
|
|
self.assertIsNotNone(found_job)
|
|
if iteration == 1:
|
|
self.assertIsNotNone(found_job['estimated_time'])
|
|
self.assertIsNone(found_job['remaining_time'])
|
|
else:
|
|
self.assertIsNotNone(found_job['estimated_time'])
|
|
self.assertTrue(found_job['estimated_time'] >= 2)
|
|
self.assertIsNotNone(found_job['remaining_time'])
|
|
|
|
self.executor_server.hold_jobs_in_build = False
|
|
self.executor_server.release()
|
|
self.waitUntilSettled()
|
|
|
|
def test_time_database(self):
|
|
"Test the time database"
|
|
|
|
self._test_time_database(1)
|
|
self._test_time_database(2)
|
|
|
|
def test_two_failed_changes_at_head(self):
|
|
"Test that changes are reparented correctly if 2 fail at head"
|
|
|
|
self.executor_server.hold_jobs_in_build = True
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
|
|
C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
|
|
A.addApproval('Code-Review', 2)
|
|
B.addApproval('Code-Review', 2)
|
|
C.addApproval('Code-Review', 2)
|
|
|
|
self.executor_server.failJob('project-test1', A)
|
|
self.executor_server.failJob('project-test1', B)
|
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
|
|
self.waitUntilSettled()
|
|
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(len(self.builds), 6)
|
|
self.assertEqual(self.builds[0].name, 'project-test1')
|
|
self.assertEqual(self.builds[1].name, 'project-test2')
|
|
self.assertEqual(self.builds[2].name, 'project-test1')
|
|
self.assertEqual(self.builds[3].name, 'project-test2')
|
|
self.assertEqual(self.builds[4].name, 'project-test1')
|
|
self.assertEqual(self.builds[5].name, 'project-test2')
|
|
|
|
self.assertTrue(self.builds[0].hasChanges(A))
|
|
self.assertTrue(self.builds[2].hasChanges(A))
|
|
self.assertTrue(self.builds[2].hasChanges(B))
|
|
self.assertTrue(self.builds[4].hasChanges(A))
|
|
self.assertTrue(self.builds[4].hasChanges(B))
|
|
self.assertTrue(self.builds[4].hasChanges(C))
|
|
|
|
# Fail change B first
|
|
self.release(self.builds[2])
|
|
self.waitUntilSettled()
|
|
|
|
# restart of C after B failure
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(len(self.builds), 5)
|
|
self.assertEqual(self.builds[0].name, 'project-test1')
|
|
self.assertEqual(self.builds[1].name, 'project-test2')
|
|
self.assertEqual(self.builds[2].name, 'project-test2')
|
|
self.assertEqual(self.builds[3].name, 'project-test1')
|
|
self.assertEqual(self.builds[4].name, 'project-test2')
|
|
|
|
self.assertTrue(self.builds[1].hasChanges(A))
|
|
self.assertTrue(self.builds[2].hasChanges(A))
|
|
self.assertTrue(self.builds[2].hasChanges(B))
|
|
self.assertTrue(self.builds[4].hasChanges(A))
|
|
self.assertFalse(self.builds[4].hasChanges(B))
|
|
self.assertTrue(self.builds[4].hasChanges(C))
|
|
|
|
# Finish running all passing jobs for change A
|
|
self.release(self.builds[1])
|
|
self.waitUntilSettled()
|
|
# Fail and report change A
|
|
self.release(self.builds[0])
|
|
self.waitUntilSettled()
|
|
|
|
# restart of B,C after A failure
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(len(self.builds), 4)
|
|
self.assertEqual(self.builds[0].name, 'project-test1') # B
|
|
self.assertEqual(self.builds[1].name, 'project-test2') # B
|
|
self.assertEqual(self.builds[2].name, 'project-test1') # C
|
|
self.assertEqual(self.builds[3].name, 'project-test2') # C
|
|
|
|
self.assertFalse(self.builds[1].hasChanges(A))
|
|
self.assertTrue(self.builds[1].hasChanges(B))
|
|
self.assertFalse(self.builds[1].hasChanges(C))
|
|
|
|
self.assertFalse(self.builds[2].hasChanges(A))
|
|
# After A failed and B and C restarted, B should be back in
|
|
# C's tests because it has not failed yet.
|
|
self.assertTrue(self.builds[2].hasChanges(B))
|
|
self.assertTrue(self.builds[2].hasChanges(C))
|
|
|
|
self.executor_server.hold_jobs_in_build = False
|
|
self.executor_server.release()
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(len(self.builds), 0)
|
|
self.assertEqual(len(self.history), 21)
|
|
self.assertEqual(A.data['status'], 'NEW')
|
|
self.assertEqual(B.data['status'], 'NEW')
|
|
self.assertEqual(C.data['status'], 'MERGED')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertEqual(B.reported, 2)
|
|
self.assertEqual(C.reported, 2)
|
|
|
|
def test_patch_order(self):
|
|
"Test that dependent patches are tested in the right order"
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
|
|
C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
|
|
A.addApproval('Code-Review', 2)
|
|
B.addApproval('Code-Review', 2)
|
|
C.addApproval('Code-Review', 2)
|
|
|
|
M2 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M2')
|
|
M1 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M1')
|
|
M2.setMerged()
|
|
M1.setMerged()
|
|
|
|
# C -> B -> A -> M1 -> M2
|
|
# M2 is here to make sure it is never queried. If it is, it
|
|
# means zuul is walking down the entire history of merged
|
|
# changes.
|
|
|
|
C.setDependsOn(B, 1)
|
|
B.setDependsOn(A, 1)
|
|
A.setDependsOn(M1, 1)
|
|
M1.setDependsOn(M2, 1)
|
|
|
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
|
|
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(A.data['status'], 'NEW')
|
|
self.assertEqual(B.data['status'], 'NEW')
|
|
self.assertEqual(C.data['status'], 'NEW')
|
|
|
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
|
|
|
|
self.waitUntilSettled()
|
|
self.assertEqual(M2.queried, 0)
|
|
self.assertEqual(A.data['status'], 'MERGED')
|
|
self.assertEqual(B.data['status'], 'MERGED')
|
|
self.assertEqual(C.data['status'], 'MERGED')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertEqual(B.reported, 2)
|
|
self.assertEqual(C.reported, 2)
|
|
|
|
def test_needed_changes_enqueue(self):
|
|
"Test that a needed change is enqueued ahead"
|
|
# A Given a git tree like this, if we enqueue
|
|
# / \ change C, we should walk up and down the tree
|
|
# B G and enqueue changes in the order ABCDEFG.
|
|
# /|\ This is also the order that you would get if
|
|
# *C E F you enqueued changes in the order ABCDEFG, so
|
|
# / the ordering is stable across re-enqueue events.
|
|
# D
|
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
|
|
C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
|
|
D = self.fake_gerrit.addFakeChange('org/project', 'master', 'D')
|
|
E = self.fake_gerrit.addFakeChange('org/project', 'master', 'E')
|
|
F = self.fake_gerrit.addFakeChange('org/project', 'master', 'F')
|
|
G = self.fake_gerrit.addFakeChange('org/project', 'master', 'G')
|
|
B.setDependsOn(A, 1)
|
|
C.setDependsOn(B, 1)
|
|
D.setDependsOn(C, 1)
|
|
E.setDependsOn(B, 1)
|
|
F.setDependsOn(B, 1)
|
|
G.setDependsOn(A, 1)
|
|
|
|
A.addApproval('Code-Review', 2)
|
|
B.addApproval('Code-Review', 2)
|
|
C.addApproval('Code-Review', 2)
|
|
D.addApproval('Code-Review', 2)
|
|
E.addApproval('Code-Review', 2)
|
|
F.addApproval('Code-Review', 2)
|
|
G.addApproval('Code-Review', 2)
|
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
|
|
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(A.data['status'], 'NEW')
|
|
self.assertEqual(B.data['status'], 'NEW')
|
|
self.assertEqual(C.data['status'], 'NEW')
|
|
self.assertEqual(D.data['status'], 'NEW')
|
|
self.assertEqual(E.data['status'], 'NEW')
|
|
self.assertEqual(F.data['status'], 'NEW')
|
|
self.assertEqual(G.data['status'], 'NEW')
|
|
|
|
# We're about to add approvals to changes without adding the
|
|
# triggering events to Zuul, so that we can be sure that it is
|
|
# enqueing the changes based on dependencies, not because of
|
|
# triggering events. Since it will have the changes cached
|
|
# already (without approvals), we need to clear the cache
|
|
# first.
|
|
for connection in self.scheds.first.connections.connections.values():
|
|
connection.maintainCache([])
|
|
|
|
self.executor_server.hold_jobs_in_build = True
|
|
A.addApproval('Approved', 1)
|
|
B.addApproval('Approved', 1)
|
|
D.addApproval('Approved', 1)
|
|
E.addApproval('Approved', 1)
|
|
F.addApproval('Approved', 1)
|
|
G.addApproval('Approved', 1)
|
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
|
|
|
|
for x in range(8):
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.executor_server.hold_jobs_in_build = False
|
|
self.executor_server.release()
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(A.data['status'], 'MERGED')
|
|
self.assertEqual(B.data['status'], 'MERGED')
|
|
self.assertEqual(C.data['status'], 'MERGED')
|
|
self.assertEqual(D.data['status'], 'MERGED')
|
|
self.assertEqual(E.data['status'], 'MERGED')
|
|
self.assertEqual(F.data['status'], 'MERGED')
|
|
self.assertEqual(G.data['status'], 'MERGED')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertEqual(B.reported, 2)
|
|
self.assertEqual(C.reported, 2)
|
|
self.assertEqual(D.reported, 2)
|
|
self.assertEqual(E.reported, 2)
|
|
self.assertEqual(F.reported, 2)
|
|
self.assertEqual(G.reported, 2)
|
|
self.assertEqual(self.history[6].changes,
|
|
'1,1 2,1 3,1 4,1 5,1 6,1 7,1')
|
|
|
|
def test_source_cache(self):
|
|
"Test that the source cache operates correctly"
|
|
self.executor_server.hold_jobs_in_build = True
|
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
|
|
X = self.fake_gerrit.addFakeChange('org/project', 'master', 'X')
|
|
A.addApproval('Code-Review', 2)
|
|
B.addApproval('Code-Review', 2)
|
|
|
|
M1 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M1')
|
|
M1.setMerged()
|
|
|
|
B.setDependsOn(A, 1)
|
|
A.setDependsOn(M1, 1)
|
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(X.getPatchsetCreatedEvent(1))
|
|
|
|
self.waitUntilSettled()
|
|
|
|
for build in self.builds:
|
|
if build.pipeline == 'check':
|
|
build.release()
|
|
self.waitUntilSettled()
|
|
for build in self.builds:
|
|
if build.pipeline == 'check':
|
|
build.release()
|
|
self.waitUntilSettled()
|
|
|
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
|
|
self.waitUntilSettled()
|
|
|
|
self.log.debug("len %s" % self.fake_gerrit._change_cache.keys())
|
|
# there should still be changes in the cache
|
|
self.assertNotEqual(len(self.fake_gerrit._change_cache.keys()), 0)
|
|
|
|
self.executor_server.hold_jobs_in_build = False
|
|
self.executor_server.release()
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(A.data['status'], 'MERGED')
|
|
self.assertEqual(B.data['status'], 'MERGED')
|
|
self.assertEqual(A.queried, 2) # Initial and isMerged
|
|
self.assertEqual(B.queried, 3) # Initial A, refresh from B, isMerged
|
|
|
|
def test_can_merge(self):
|
|
"Test whether a change is ready to merge"
|
|
# TODO: move to test_gerrit (this is a unit test!)
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
tenant = self.scheds.first.sched.abide.tenants.get('tenant-one')
|
|
(trusted, project) = tenant.getProject('org/project')
|
|
source = project.source
|
|
|
|
# TODO(pabelanger): As we add more source / trigger APIs we should make
|
|
# it easier for users to create events for testing.
|
|
event = zuul.model.TriggerEvent()
|
|
event.trigger_name = 'gerrit'
|
|
event.change_number = '1'
|
|
event.patch_number = '2'
|
|
|
|
a = source.getChange(event)
|
|
mgr = tenant.layout.pipelines['gate'].manager
|
|
self.assertFalse(source.canMerge(a, mgr.getSubmitAllowNeeds()))
|
|
|
|
A.addApproval('Code-Review', 2)
|
|
a = source.getChange(event, refresh=True)
|
|
self.assertFalse(source.canMerge(a, mgr.getSubmitAllowNeeds()))
|
|
|
|
A.addApproval('Approved', 1)
|
|
a = source.getChange(event, refresh=True)
|
|
self.assertTrue(source.canMerge(a, mgr.getSubmitAllowNeeds()))
|
|
|
|
def test_project_merge_conflict(self):
|
|
"Test that gate merge conflicts are handled properly"
|
|
|
|
self.gearman_server.hold_jobs_in_queue = True
|
|
A = self.fake_gerrit.addFakeChange('org/project',
|
|
'master', 'A',
|
|
files={'conflict': 'foo'})
|
|
B = self.fake_gerrit.addFakeChange('org/project',
|
|
'master', 'B',
|
|
files={'conflict': 'bar'})
|
|
C = self.fake_gerrit.addFakeChange('org/project',
|
|
'master', 'C')
|
|
A.addApproval('Code-Review', 2)
|
|
B.addApproval('Code-Review', 2)
|
|
C.addApproval('Code-Review', 2)
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(A.reported, 1)
|
|
self.assertEqual(C.reported, 1)
|
|
|
|
self.gearman_server.release('project-merge')
|
|
self.waitUntilSettled()
|
|
self.gearman_server.release('project-merge')
|
|
self.waitUntilSettled()
|
|
self.gearman_server.release('project-merge')
|
|
self.waitUntilSettled()
|
|
|
|
self.gearman_server.hold_jobs_in_queue = False
|
|
self.gearman_server.release()
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(A.data['status'], 'MERGED')
|
|
self.assertEqual(B.data['status'], 'NEW')
|
|
self.assertEqual(C.data['status'], 'MERGED')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertIn('Merge Failed', B.messages[-1])
|
|
self.assertEqual(C.reported, 2)
|
|
|
|
self.assertHistory([
|
|
dict(name='project-merge', result='SUCCESS', changes='1,1'),
|
|
dict(name='project-test1', result='SUCCESS', changes='1,1'),
|
|
dict(name='project-test2', result='SUCCESS', changes='1,1'),
|
|
dict(name='project-merge', result='SUCCESS', changes='1,1 3,1'),
|
|
dict(name='project-test1', result='SUCCESS', changes='1,1 3,1'),
|
|
dict(name='project-test2', result='SUCCESS', changes='1,1 3,1'),
|
|
], ordered=False)
|
|
|
|
def test_delayed_merge_conflict(self):
|
|
"Test that delayed check merge conflicts are handled properly"
|
|
|
|
# Hold jobs in the gearman queue so that we can test whether
|
|
# the executor sucesfully merges a change based on an old
|
|
# repo state (frozen by the scheduler) which would otherwise
|
|
# conflict.
|
|
self.gearman_server.hold_jobs_in_queue = True
|
|
A = self.fake_gerrit.addFakeChange('org/project',
|
|
'master', 'A',
|
|
files={'conflict': 'foo'})
|
|
B = self.fake_gerrit.addFakeChange('org/project',
|
|
'master', 'B',
|
|
files={'conflict': 'bar'})
|
|
C = self.fake_gerrit.addFakeChange('org/project',
|
|
'master', 'C')
|
|
C.setDependsOn(B, 1)
|
|
|
|
# A enters the gate queue; B and C enter the check queue
|
|
A.addApproval('Code-Review', 2)
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
|
|
self.waitUntilSettled()
|
|
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
|
|
self.waitUntilSettled()
|
|
self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(1))
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(A.reported, 1)
|
|
self.assertEqual(B.reported, 0) # Check does not report start
|
|
self.assertEqual(C.reported, 0) # Check does not report start
|
|
|
|
# A merges while B and C are queued in check
|
|
# Release A project-merge
|
|
queue = self.gearman_server.getQueue()
|
|
self.release(queue[0])
|
|
self.waitUntilSettled()
|
|
|
|
# Release A project-test*
|
|
# gate has higher precedence, so A's test jobs are added in
|
|
# front of the merge jobs for B and C
|
|
queue = self.gearman_server.getQueue()
|
|
self.release(queue[0])
|
|
self.release(queue[1])
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(A.data['status'], 'MERGED')
|
|
self.assertEqual(B.data['status'], 'NEW')
|
|
self.assertEqual(C.data['status'], 'NEW')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertEqual(B.reported, 0)
|
|
self.assertEqual(C.reported, 0)
|
|
self.assertHistory([
|
|
dict(name='project-merge', result='SUCCESS', changes='1,1'),
|
|
dict(name='project-test1', result='SUCCESS', changes='1,1'),
|
|
dict(name='project-test2', result='SUCCESS', changes='1,1'),
|
|
], ordered=False)
|
|
|
|
# B and C report merge conflicts
|
|
# Release B project-merge
|
|
queue = self.gearman_server.getQueue()
|
|
self.release(queue[0])
|
|
self.waitUntilSettled()
|
|
|
|
# Release C
|
|
self.gearman_server.hold_jobs_in_queue = False
|
|
self.gearman_server.release()
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(A.data['status'], 'MERGED')
|
|
self.assertEqual(B.data['status'], 'NEW')
|
|
self.assertEqual(C.data['status'], 'NEW')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertEqual(B.reported, 1)
|
|
self.assertEqual(C.reported, 1)
|
|
|
|
self.assertHistory([
|
|
dict(name='project-merge', result='SUCCESS', changes='1,1'),
|
|
dict(name='project-test1', result='SUCCESS', changes='1,1'),
|
|
dict(name='project-test2', result='SUCCESS', changes='1,1'),
|
|
dict(name='project-merge', result='SUCCESS', changes='2,1'),
|
|
dict(name='project-test1', result='SUCCESS', changes='2,1'),
|
|
dict(name='project-test2', result='SUCCESS', changes='2,1'),
|
|
dict(name='project-merge', result='SUCCESS', changes='2,1 3,1'),
|
|
dict(name='project-test1', result='SUCCESS', changes='2,1 3,1'),
|
|
dict(name='project-test2', result='SUCCESS', changes='2,1 3,1'),
|
|
], ordered=False)
|
|
|
|
def test_post(self):
|
|
"Test that post jobs run"
|
|
p = "review.example.com/org/project"
|
|
upstream = self.getUpstreamRepos([p])
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
A.setMerged()
|
|
A_commit = str(upstream[p].commit('master'))
|
|
self.log.debug("A commit: %s" % A_commit)
|
|
|
|
e = {
|
|
"type": "ref-updated",
|
|
"submitter": {
|
|
"name": "User Name",
|
|
},
|
|
"refUpdate": {
|
|
"oldRev": "90f173846e3af9154517b88543ffbd1691f31366",
|
|
"newRev": A_commit,
|
|
"refName": "master",
|
|
"project": "org/project",
|
|
}
|
|
}
|
|
self.fake_gerrit.addEvent(e)
|
|
self.waitUntilSettled()
|
|
|
|
job_names = [x.name for x in self.history]
|
|
self.assertEqual(len(self.history), 1)
|
|
self.assertIn('project-post', job_names)
|
|
|
|
def test_post_ignore_deletes(self):
|
|
"Test that deleting refs does not trigger post jobs"
|
|
|
|
e = {
|
|
"type": "ref-updated",
|
|
"submitter": {
|
|
"name": "User Name",
|
|
},
|
|
"refUpdate": {
|
|
"oldRev": "90f173846e3af9154517b88543ffbd1691f31366",
|
|
"newRev": "0000000000000000000000000000000000000000",
|
|
"refName": "master",
|
|
"project": "org/project",
|
|
}
|
|
}
|
|
self.fake_gerrit.addEvent(e)
|
|
self.waitUntilSettled()
|
|
|
|
job_names = [x.name for x in self.history]
|
|
self.assertEqual(len(self.history), 0)
|
|
self.assertNotIn('project-post', job_names)
|
|
|
|
@simple_layout('layouts/dont-ignore-ref-deletes.yaml')
|
|
def test_post_ignore_deletes_negative(self):
|
|
"Test that deleting refs does trigger post jobs"
|
|
e = {
|
|
"type": "ref-updated",
|
|
"submitter": {
|
|
"name": "User Name",
|
|
},
|
|
"refUpdate": {
|
|
"oldRev": "90f173846e3af9154517b88543ffbd1691f31366",
|
|
"newRev": "0000000000000000000000000000000000000000",
|
|
"refName": "testbranch",
|
|
"project": "org/project",
|
|
}
|
|
}
|
|
self.fake_gerrit.addEvent(e)
|
|
self.waitUntilSettled()
|
|
|
|
job_names = [x.name for x in self.history]
|
|
self.assertEqual(len(self.history), 1)
|
|
self.assertIn('project-post', job_names)
|
|
|
|
@skip("Disabled for early v3 development")
|
|
def test_build_configuration_branch_interaction(self):
|
|
"Test that switching between branches works"
|
|
self.test_build_configuration()
|
|
self.test_build_configuration_branch()
|
|
# C has been merged, undo that
|
|
path = os.path.join(self.upstream_root, "org/project")
|
|
repo = git.Repo(path)
|
|
repo.heads.master.commit = repo.commit('init')
|
|
self.test_build_configuration()
|
|
|
|
def test_dependent_changes_rebase(self):
|
|
# Test that no errors occur when we walk a dependency tree
|
|
# with an unused leaf node due to a rebase.
|
|
# Start by constructing: C -> B -> A
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
|
|
B.setDependsOn(A, 1)
|
|
|
|
C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
|
|
C.setDependsOn(B, 1)
|
|
|
|
# Then rebase to form: D -> C -> A
|
|
C.addPatchset() # C,2
|
|
C.setDependsOn(A, 1)
|
|
|
|
D = self.fake_gerrit.addFakeChange('org/project', 'master', 'D')
|
|
D.setDependsOn(C, 2)
|
|
|
|
# Walk the entire tree
|
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
|
|
self.waitUntilSettled()
|
|
self.assertEqual(len(self.history), 3)
|
|
|
|
# Verify that walking just part of the tree still works
|
|
self.fake_gerrit.addEvent(D.getPatchsetCreatedEvent(1))
|
|
self.waitUntilSettled()
|
|
self.assertEqual(len(self.history), 6)
|
|
|
|
def test_dependent_changes_dequeue(self):
|
|
"Test that dependent patches are not needlessly tested"
|
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
|
|
C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
|
|
A.addApproval('Code-Review', 2)
|
|
B.addApproval('Code-Review', 2)
|
|
C.addApproval('Code-Review', 2)
|
|
|
|
M1 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M1')
|
|
M1.setMerged()
|
|
|
|
# C -> B -> A -> M1
|
|
|
|
C.setDependsOn(B, 1)
|
|
B.setDependsOn(A, 1)
|
|
A.setDependsOn(M1, 1)
|
|
|
|
self.executor_server.failJob('project-merge', A)
|
|
|
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
|
|
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(A.data['status'], 'NEW')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertEqual(B.data['status'], 'NEW')
|
|
self.assertEqual(B.reported, 2)
|
|
self.assertEqual(C.data['status'], 'NEW')
|
|
self.assertIn('This change depends on a change that failed to merge.',
|
|
C.messages[-1])
|
|
self.assertEqual(len(self.history), 1)
|
|
|
|
def test_failing_dependent_changes(self):
|
|
"Test that failing dependent patches are taken out of stream"
|
|
self.executor_server.hold_jobs_in_build = True
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
|
|
C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
|
|
D = self.fake_gerrit.addFakeChange('org/project', 'master', 'D')
|
|
E = self.fake_gerrit.addFakeChange('org/project', 'master', 'E')
|
|
A.addApproval('Code-Review', 2)
|
|
B.addApproval('Code-Review', 2)
|
|
C.addApproval('Code-Review', 2)
|
|
D.addApproval('Code-Review', 2)
|
|
E.addApproval('Code-Review', 2)
|
|
|
|
# E, D -> C -> B, A
|
|
|
|
D.setDependsOn(C, 1)
|
|
C.setDependsOn(B, 1)
|
|
|
|
self.executor_server.failJob('project-test1', B)
|
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(D.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(E.addApproval('Approved', 1))
|
|
|
|
self.waitUntilSettled()
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
|
|
self.executor_server.hold_jobs_in_build = False
|
|
for build in self.builds:
|
|
if build.parameters['zuul']['change'] != '1':
|
|
build.release()
|
|
self.waitUntilSettled()
|
|
|
|
self.executor_server.release()
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(A.data['status'], 'MERGED')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertIn('Build succeeded', A.messages[1])
|
|
self.assertEqual(B.data['status'], 'NEW')
|
|
self.assertEqual(B.reported, 2)
|
|
self.assertIn('Build failed', B.messages[1])
|
|
self.assertEqual(C.data['status'], 'NEW')
|
|
self.assertEqual(C.reported, 2)
|
|
self.assertIn('depends on a change', C.messages[1])
|
|
self.assertEqual(D.data['status'], 'NEW')
|
|
self.assertEqual(D.reported, 2)
|
|
self.assertIn('depends on a change', D.messages[1])
|
|
self.assertEqual(E.data['status'], 'MERGED')
|
|
self.assertEqual(E.reported, 2)
|
|
self.assertIn('Build succeeded', E.messages[1])
|
|
self.assertEqual(len(self.history), 18)
|
|
|
|
def test_head_is_dequeued_once(self):
|
|
"Test that if a change at the head fails it is dequeued only once"
|
|
# If it's dequeued more than once, we should see extra
|
|
# aborted jobs.
|
|
|
|
self.executor_server.hold_jobs_in_build = True
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
|
|
C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
|
|
A.addApproval('Code-Review', 2)
|
|
B.addApproval('Code-Review', 2)
|
|
C.addApproval('Code-Review', 2)
|
|
|
|
self.executor_server.failJob('project-test1', A)
|
|
self.executor_server.failJob('project-test2', A)
|
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
|
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
|
|
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(len(self.builds), 1)
|
|
self.assertEqual(self.builds[0].name, 'project-merge')
|
|
self.assertTrue(self.builds[0].hasChanges(A))
|
|
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
self.executor_server.release('.*-merge')
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(len(self.builds), 6)
|
|
self.assertEqual(self.builds[0].name, 'project-test1')
|
|
self.assertEqual(self.builds[1].name, 'project-test2')
|
|
self.assertEqual(self.builds[2].name, 'project-test1')
|
|
self.assertEqual(self.builds[3].name, 'project-test2')
|
|
self.assertEqual(self.builds[4].name, 'project-test1')
|
|
self.assertEqual(self.builds[5].name, 'project-test2')
|
|
|
|
self.release(self.builds[0])
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(len(self.builds), 2) # test2, merge for B
|
|
self.assertEqual(self.countJobResults(self.history, 'ABORTED'), 4)
|
|
|
|
self.executor_server.hold_jobs_in_build = False
|
|
self.executor_server.release()
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(len(self.builds), 0)
|
|
self.assertEqual(len(self.history), 15)
|
|
|
|
self.assertEqual(A.data['status'], 'NEW')
|
|
self.assertEqual(B.data['status'], 'MERGED')
|
|
self.assertEqual(C.data['status'], 'MERGED')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertEqual(B.reported, 2)
|
|
self.assertEqual(C.reported, 2)
|
|
|
|
@simple_layout('layouts/nonvoting-job-approval.yaml')
|
|
def test_nonvoting_job_approval(self):
|
|
"Test that non-voting jobs don't vote but leave approval"
|
|
|
|
A = self.fake_gerrit.addFakeChange('org/nonvoting-project',
|
|
'master', 'A')
|
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
|
|
self.executor_server.failJob('nonvoting-project-test2', A)
|
|
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(A.data['status'], 'NEW')
|
|
self.assertEqual(A.reported, 1)
|
|
|
|
self.assertEqual(
|
|
self.getJobFromHistory('nonvoting-project-test1').result,
|
|
'SUCCESS')
|
|
self.assertEqual(
|
|
self.getJobFromHistory('nonvoting-project-test2').result,
|
|
'FAILURE')
|
|
|
|
self.assertFalse(self.getJobFromHistory('nonvoting-project-test1').
|
|
parameters['zuul']['voting'])
|
|
self.assertFalse(self.getJobFromHistory('nonvoting-project-test2').
|
|
parameters['zuul']['voting'])
|
|
self.assertEqual(A.patchsets[0]['approvals'][0]['value'], "1")
|
|
|
|
@simple_layout('layouts/nonvoting-job.yaml')
|
|
def test_nonvoting_job(self):
|
|
"Test that non-voting jobs don't vote."
|
|
|
|
A = self.fake_gerrit.addFakeChange('org/nonvoting-project',
|
|
'master', 'A')
|
|
A.addApproval('Code-Review', 2)
|
|
self.executor_server.failJob('nonvoting-project-test2', A)
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
|
|
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(A.data['status'], 'MERGED')
|
|
self.assertEqual(A.reported, 2)
|
|
self.assertEqual(
|
|
self.getJobFromHistory('nonvoting-project-merge').result,
|
|
'SUCCESS')
|
|
self.assertEqual(
|
|
self.getJobFromHistory('nonvoting-project-test1').result,
|
|
'SUCCESS')
|
|
self.assertEqual(
|
|
self.getJobFromHistory('nonvoting-project-test2').result,
|
|
'FAILURE')
|
|
|
|
self.assertTrue(self.getJobFromHistory('nonvoting-project-merge').
|
|
parameters['zuul']['voting'])
|
|
self.assertTrue(self.getJobFromHistory('nonvoting-project-test1').
|
|
parameters['zuul']['voting'])
|
|
self.assertFalse(self.getJobFromHistory('nonvoting-project-test2').
|
|
parameters['zuul']['voting'])
|
|
|
|
def test_check_queue_success(self):
|
|
"Test successful check queue jobs."
|
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
|
|
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(A.data['status'], 'NEW')
|
|
self.assertEqual(A.reported, 1)
|
|
self.assertEqual(self.getJobFromHistory('project-merge').result,
|
|
'SUCCESS')
|
|
self.assertEqual(self.getJobFromHistory('project-test1').result,
|
|
'SUCCESS')
|
|
self.assertEqual(self.getJobFromHistory('project-test2').result,
|
|
'SUCCESS')
|
|
|
|
def test_check_queue_failure(self):
|
|
"Test failed check queue jobs."
|
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
self.executor_server.failJob('project-test2', A)
|
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
|
|
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(A.data['status'], 'NEW')
|
|
self.assertEqual(A.reported, 1)
|
|
self.assertEqual(self.getJobFromHistory('project-merge').result,
|
|
'SUCCESS')
|
|
self.assertEqual(self.getJobFromHistory('project-test1').result,
|
|
'SUCCESS')
|
|
self.assertEqual(self.getJobFromHistory('project-test2').result,
|
|
'FAILURE')
|
|
|
|
@simple_layout('layouts/autohold.yaml')
|
|
def test_autohold(self):
|
|
client = zuul.rpcclient.RPCClient('127.0.0.1',
|
|
self.gearman_server.port)
|
|
self.addCleanup(client.shutdown)
|
|
r = client.autohold('tenant-one', 'org/project', 'project-test2',
|
|
"", "", "reason text", 1)
|
|
self.assertTrue(r)
|
|
|
|
# There should be a record in ZooKeeper
|
|
request_list = self.scheds.first.sched.zk.getHoldRequests()
|
|
self.assertEqual(1, len(request_list))
|
|
request = self.scheds.first.sched.zk.getHoldRequest(request_list[0])
|
|
self.assertIsNotNone(request)
|
|
self.assertEqual('tenant-one', request.tenant)
|
|
self.assertEqual('review.example.com/org/project', request.project)
|
|
self.assertEqual('project-test2', request.job)
|
|
self.assertEqual('reason text', request.reason)
|
|
self.assertEqual(1, request.max_count)
|
|
self.assertEqual(0, request.current_count)
|
|
self.assertEqual([], request.nodes)
|
|
|
|
# First check that successful jobs do not autohold
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
|
|
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(A.data['status'], 'NEW')
|
|
self.assertEqual(A.reported, 1)
|
|
# project-test2
|
|
self.assertEqual(self.history[0].result, 'SUCCESS')
|
|
|
|
# Check nodepool for a held node
|
|
held_node = None
|
|
for node in self.fake_nodepool.getNodes():
|
|
if node['state'] == zuul.model.STATE_HOLD:
|
|
held_node = node
|
|
break
|
|
self.assertIsNone(held_node)
|
|
|
|
# Now test that failed jobs are autoheld
|
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
|
|
self.executor_server.failJob('project-test2', B)
|
|
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
|
|
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(B.data['status'], 'NEW')
|
|
self.assertEqual(B.reported, 1)
|
|
# project-test2
|
|
self.assertEqual(self.history[1].result, 'FAILURE')
|
|
|
|
# Check nodepool for a held node
|
|
held_node = None
|
|
for node in self.fake_nodepool.getNodes():
|
|
if node['state'] == zuul.model.STATE_HOLD:
|
|
held_node = node
|
|
break
|
|
self.assertIsNotNone(held_node)
|
|
# Validate node has recorded the failed job
|
|
self.assertEqual(
|
|
held_node['hold_job'],
|
|
" ".join(['tenant-one',
|
|
'review.example.com/org/project',
|
|
'project-test2', '.*'])
|
|
)
|
|
self.assertEqual(held_node['comment'], "reason text")
|
|
|
|
# The hold request current_count should have incremented
|
|
# and we should have recorded the held node ID.
|
|
request2 = self.scheds.first.sched.zk.getHoldRequest(request.id)
|
|
self.assertEqual(request.current_count + 1, request2.current_count)
|
|
self.assertEqual(1, len(request2.nodes))
|
|
self.assertEqual(1, len(request2.nodes[0]["nodes"]))
|
|
|
|
# Another failed change should not hold any more nodes
|
|
C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
|
|
self.executor_server.failJob('project-test2', C)
|
|
self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(1))
|
|
self.waitUntilSettled()
|
|
self.assertEqual(C.data['status'], 'NEW')
|
|
self.assertEqual(C.reported, 1)
|
|
# project-test2
|
|
self.assertEqual(self.history[2].result, 'FAILURE')
|
|
|
|
held_nodes = 0
|
|
for node in self.fake_nodepool.getNodes():
|
|
if node['state'] == zuul.model.STATE_HOLD:
|
|
held_nodes += 1
|
|
self.assertEqual(held_nodes, 1)
|
|
|
|
# request current_count should not have changed
|
|
request3 = self.scheds.first.sched.zk.getHoldRequest(request2.id)
|
|
self.assertEqual(request2.current_count, request3.current_count)
|
|
|
|
# Deleting hold request should set held nodes to used
|
|
self.scheds.first.sched.zk.deleteHoldRequest(request3)
|
|
node_states = [n['state'] for n in self.fake_nodepool.getNodes()]
|
|
self.assertEqual(3, len(node_states))
|
|
self.assertEqual([zuul.model.STATE_USED] * 3, node_states)
|
|
|
|
@simple_layout('layouts/autohold.yaml')
|
|
def test_autohold_info(self):
|
|
client = zuul.rpcclient.RPCClient('127.0.0.1',
|
|
self.gearman_server.port)
|
|
self.addCleanup(client.shutdown)
|
|
|
|
# Empty dict should be returned for "not found"
|
|
request = client.autohold_info("XxXxX")
|
|
self.assertEqual({}, request)
|
|
|
|
r = client.autohold('tenant-one', 'org/project', 'project-test2',
|
|
"", "", "reason text", 1)
|
|
self.assertTrue(r)
|
|
|
|
# There should be a record in ZooKeeper
|
|
request_list = self.scheds.first.sched.zk.getHoldRequests()
|
|
self.assertEqual(1, len(request_list))
|
|
request = self.scheds.first.sched.zk.getHoldRequest(request_list[0])
|
|
self.assertIsNotNone(request)
|
|
|
|
request = client.autohold_info(request.id)
|
|
self.assertNotEqual({}, request)
|
|
self.assertEqual('tenant-one', request['tenant'])
|
|
self.assertEqual('review.example.com/org/project', request['project'])
|
|
self.assertEqual('project-test2', request['job'])
|
|
self.assertEqual('reason text', request['reason'])
|
|
self.assertEqual(1, request['max_count'])
|
|
self.assertEqual(0, request['current_count'])
|
|
|
|
@simple_layout('layouts/autohold.yaml')
|
|
def test_autohold_delete(self):
|
|
client = zuul.rpcclient.RPCClient('127.0.0.1',
|
|
self.gearman_server.port)
|
|
self.addCleanup(client.shutdown)
|
|
r = client.autohold('tenant-one', 'org/project', 'project-test2',
|
|
"", "", "reason text", 1)
|
|
self.assertTrue(r)
|
|
|
|
# There should be a record in ZooKeeper
|
|
request_list = self.scheds.first.sched.zk.getHoldRequests()
|
|
self.assertEqual(1, len(request_list))
|
|
request = self.scheds.first.sched.zk.getHoldRequest(request_list[0])
|
|
self.assertIsNotNone(request)
|
|
|
|
# Delete and verify no more requests
|
|
self.assertTrue(client.autohold_delete(request.id))
|
|
request_list = self.scheds.first.sched.zk.getHoldRequests()
|
|
self.assertEqual([], request_list)
|
|
|
|
def _test_autohold_scoped(self, change_obj, change, ref):
|
|
client = zuul.rpcclient.RPCClient('127.0.0.1',
|
|
self.gearman_server.port)
|
|
self.addCleanup(client.shutdown)
|
|
|
|
# create two changes on the same project, and autohold request
|
|
# for one of them.
|
|
other = self.fake_gerrit.addFakeChange(
|
|
'org/project', 'master', 'other'
|
|
)
|
|
|
|
r = client.autohold('tenant-one', 'org/project', 'project-test2',
|
|
str(change), ref, "reason text", 1)
|
|
self.assertTrue(r)
|
|
|
|
# First, check that an unrelated job does not trigger autohold, even
|
|
# when it failed
|
|
self.executor_server.failJob('project-test2', other)
|
|
self.fake_gerrit.addEvent(other.getPatchsetCreatedEvent(1))
|
|
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(other.data['status'], 'NEW')
|
|
self.assertEqual(other.reported, 1)
|
|
# project-test2
|
|
self.assertEqual(self.history[0].result, 'FAILURE')
|
|
|
|
# Check nodepool for a held node
|
|
held_node = None
|
|
for node in self.fake_nodepool.getNodes():
|
|
if node['state'] == zuul.model.STATE_HOLD:
|
|
held_node = node
|
|
break
|
|
self.assertIsNone(held_node)
|
|
|
|
# And then verify that failed job for the defined change
|
|
# triggers the autohold
|
|
|
|
self.executor_server.failJob('project-test2', change_obj)
|
|
self.fake_gerrit.addEvent(change_obj.getPatchsetCreatedEvent(1))
|
|
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(change_obj.data['status'], 'NEW')
|
|
self.assertEqual(change_obj.reported, 1)
|
|
# project-test2
|
|
self.assertEqual(self.history[1].result, 'FAILURE')
|
|
|
|
# Check nodepool for a held node
|
|
held_node = None
|
|
for node in self.fake_nodepool.getNodes():
|
|
if node['state'] == zuul.model.STATE_HOLD:
|
|
held_node = node
|
|
break
|
|
self.assertIsNotNone(held_node)
|
|
|
|
# Validate node has recorded the failed job
|
|
if change != "":
|
|
ref = "refs/changes/%s/%s/.*" % (
|
|
str(change_obj.number)[-1:], str(change_obj.number)
|
|
)
|
|
|
|
self.assertEqual(
|
|
held_node['hold_job'],
|
|
" ".join(['tenant-one',
|
|
'review.example.com/org/project',
|
|
'project-test2', ref])
|
|
)
|
|
self.assertEqual(held_node['comment'], "reason text")
|
|
|
|
@simple_layout('layouts/autohold.yaml')
|
|
def test_autohold_change(self):
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
|
|
self._test_autohold_scoped(A, change=A.number, ref="")
|
|
|
|
@simple_layout('layouts/autohold.yaml')
|
|
def test_autohold_ref(self):
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
ref = A.data['currentPatchSet']['ref']
|
|
self._test_autohold_scoped(A, change="", ref=ref)
|
|
|
|
@simple_layout('layouts/autohold.yaml')
|
|
def test_autohold_scoping(self):
|
|
client = zuul.rpcclient.RPCClient('127.0.0.1',
|
|
self.gearman_server.port)
|
|
self.addCleanup(client.shutdown)
|
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
|
|
# create three autohold requests, scoped to job, change and
|
|
# a specific ref
|
|
change = str(A.number)
|
|
ref = A.data['currentPatchSet']['ref']
|
|
r1 = client.autohold('tenant-one', 'org/project', 'project-test2',
|
|
"", "", "reason text", 1)
|
|
self.assertTrue(r1)
|
|
r2 = client.autohold('tenant-one', 'org/project', 'project-test2',
|
|
change, "", "reason text", 1)
|
|
self.assertTrue(r2)
|
|
r3 = client.autohold('tenant-one', 'org/project', 'project-test2',
|
|
"", ref, "reason text", 1)
|
|
self.assertTrue(r3)
|
|
|
|
# Fail 3 jobs for the same change, and verify that the autohold
|
|
# requests are fullfilled in the expected order: from the most
|
|
# specific towards the most generic one.
|
|
|
|
def _fail_job_and_verify_autohold_request(change_obj, ref_filter):
|
|
self.executor_server.failJob('project-test2', change_obj)
|
|
self.fake_gerrit.addEvent(change_obj.getPatchsetCreatedEvent(1))
|
|
|
|
self.waitUntilSettled()
|
|
|
|
# Check nodepool for a held node
|
|
held_node = None
|
|
for node in self.fake_nodepool.getNodes():
|
|
if node['state'] == zuul.model.STATE_HOLD:
|
|
held_node = node
|
|
break
|
|
self.assertIsNotNone(held_node)
|
|
|
|
self.assertEqual(
|
|
held_node['hold_job'],
|
|
" ".join(['tenant-one',
|
|
'review.example.com/org/project',
|
|
'project-test2', ref_filter])
|
|
)
|
|
self.assertFalse(held_node['_lock'], "Node %s is locked" %
|
|
(node['_oid'],))
|
|
self.fake_nodepool.removeNode(held_node)
|
|
|
|
_fail_job_and_verify_autohold_request(A, ref)
|
|
|
|
ref = "refs/changes/%s/%s/.*" % (str(change)[-1:], str(change))
|
|
_fail_job_and_verify_autohold_request(A, ref)
|
|
_fail_job_and_verify_autohold_request(A, ".*")
|
|
|
|
@simple_layout('layouts/autohold.yaml')
|
|
def test_autohold_ignores_aborted_jobs(self):
|
|
client = zuul.rpcclient.RPCClient('127.0.0.1',
|
|
self.gearman_server.port)
|
|
self.addCleanup(client.shutdown)
|
|
r = client.autohold('tenant-one', 'org/project', 'project-test2',
|
|
"", "", "reason text", 1)
|
|
self.assertTrue(r)
|
|
|
|
self.executor_server.hold_jobs_in_build = True
|
|
|
|
# Create a change that will have its job aborted
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
|
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
|
|
self.waitUntilSettled()
|
|
|
|
# Creating new patchset on change A will abort A,1's job because
|
|
# a new patchset arrived replacing A,1 with A,2.
|
|
A.addPatchset()
|
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(2))
|
|
|
|
self.waitUntilSettled()
|
|
self.executor_server.hold_jobs_in_build = False
|
|
self.executor_server.release()
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(A.data['status'], 'NEW')
|
|
# Note only the successful job for A,2 will report as we don't
|
|
# report aborted builds for old patchsets.
|
|
self.assertEqual(A.reported, 1)
|
|
# A,1 project-test2
|
|
self.assertEqual(self.history[0].result, 'ABORTED')
|
|
# A,2 project-test2
|
|
self.assertEqual(self.history[1].result, 'SUCCESS')
|
|
|
|
# Check nodepool for a held node
|
|
held_node = None
|
|
for node in self.fake_nodepool.getNodes():
|
|
if node['state'] == zuul.model.STATE_HOLD:
|
|
held_node = node
|
|
break
|
|
self.assertIsNone(held_node)
|
|
|
|
@simple_layout('layouts/autohold.yaml')
|
|
def test_autohold_hold_expiration(self):
|
|
client = zuul.rpcclient.RPCClient('127.0.0.1',
|
|
self.gearman_server.port)
|
|
self.addCleanup(client.shutdown)
|
|
r = client.autohold('tenant-one', 'org/project', 'project-test2',
|
|
"", "", "reason text", 1, node_hold_expiration=30)
|
|
self.assertTrue(r)
|
|
|
|
# Hold a failed job
|
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
|
|
self.executor_server.failJob('project-test2', B)
|
|
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
|
|
|
|
self.waitUntilSettled()
|
|
|
|
self.assertEqual(B.data['status'], 'NEW')
|
|
self.assertEqual(B.reported, 1)
|
|
# project-test2
|
|
self.assertEqual(self.history[0].result, 'FAILURE')
|
|
|
|
# Check nodepool for a held node
|
|
held_node = None
|
|
for node in self.fake_nodepool.getNodes():
|
|
if node['state'] == zuul.model.STATE_HOLD:
|
|
held_node = node
|
|
break
|
|
self.assertIsNotNone(held_node)
|
|
|
|
# Validate node has hold_expiration property
|
|
self.assertEqual(int(held_node['hold_expiration']), 30)
|
|
|
|
@simple_layout('layouts/autohold.yaml')
|
|
def test_autohold_list(self):
|
|
client = zuul.rpcclient.RPCClient('127.0.0.1',
|
|
self.gearman_server.port)
|
|
self.addCleanup(client.shutdown)
|
|
|
|
r = client.autohold('tenant-one', 'org/project', 'project-test2',
|
|
"", "", "reason text", 1)
|
|
self.assertTrue(r)
|
|
|
|
autohold_requests = client.autohold_list()
|
|
self.assertNotEqual([], autohold_requests)
|
|
self.assertEqual(1, len(autohold_requests))
|
|
|
|
request = autohold_requests[0]
|
|
self.assertEqual('tenant-one', request['tenant'])
|
|
self.assertIn('org/project', request['project'])
|
|
self.assertEqual( |