Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(3209): Honor freeze windows for virtual jobs #3210

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 16 additions & 22 deletions plugins/builds/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const {
createEvent,
parseJobInfo,
ensureStageTeardownBuildExists,
getJobId,
getJob,
isOrTrigger,
extractExternalJoinData,
extractCurrentPipelineJoinData,
Expand Down Expand Up @@ -93,9 +93,9 @@ async function triggerNextJobs(config, app) {

const downstreamOfNextJobsToBeProcessed = [];

for (const [nextJobName, nextJob] of Object.entries(currentPipelineNextJobs)) {
const nextJobId = nextJob.id || (await getJobId(nextJobName, currentPipeline.id, jobFactory));
const { isVirtual: isNextJobVirtual, stageName: nextJobStageName } = nextJob;
for (const [nextJobName, nextJobInfo] of Object.entries(currentPipelineNextJobs)) {
const nextJob = await getJob(nextJobName, currentPipeline.id, jobFactory);
const { isVirtual: isNextJobVirtual, stageName: nextJobStageName } = nextJobInfo;
const resource = `pipeline:${currentPipeline.id}:groupEvent:${currentEvent.groupEventId}`;
let lock;
let nextBuild;
Expand Down Expand Up @@ -124,15 +124,13 @@ async function triggerNextJobs(config, app) {
nextBuild = await orTrigger.execute(
currentEvent,
currentPipeline.id,
nextJobName,
nextJobId,
nextJob,
parentBuilds,
isNextJobVirtual
);
} else {
nextBuild = await andTrigger.execute(
nextJobName,
nextJobId,
nextJob,
parentBuilds,
joinListNames,
isNextJobVirtual,
Expand All @@ -144,7 +142,7 @@ async function triggerNextJobs(config, app) {
downstreamOfNextJobsToBeProcessed.push({
build: nextBuild,
event: currentEvent,
job: await nextBuild.job,
job: nextJob,
pipeline: currentPipeline,
scmContext: config.scmContext,
username: config.username
Expand Down Expand Up @@ -243,9 +241,9 @@ async function triggerNextJobs(config, app) {

// Skip trigger process if createExternalEvent fails
if (externalEvent) {
for (const [nextJobName, nextJob] of Object.entries(joinedPipeline.jobs)) {
const nextJobId = nextJob.id || (await getJobId(nextJobName, currentPipeline.id, jobFactory));
const { isVirtual: isNextJobVirtual, stageName: nextJobStageName } = nextJob;
for (const [nextJobName, nextJobInfo] of Object.entries(joinedPipeline.jobs)) {
const nextJob = await getJob(nextJobName, currentPipeline.id, jobFactory);
const { isVirtual: isNextJobVirtual, stageName: nextJobStageName } = nextJobInfo;

const { parentBuilds } = parseJobInfo({
joinObj: joinedPipeline.jobs,
Expand All @@ -265,23 +263,21 @@ async function triggerNextJobs(config, app) {
nextBuild = await remoteTrigger.execute(
externalEvent,
externalEvent.pipelineId,
nextJobName,
nextJobId,
nextJob,
parentBuilds,
isNextJobVirtual
);
} else {
// Re get join list when first time remote trigger since external event was empty and cannot get workflow graph then
const joinList =
nextJob.join.length > 0
? nextJob.join
nextJobInfo.join.length > 0
? nextJobInfo.join
: workflowParser.getSrcForJoin(externalEvent.workflowGraph, { jobName: nextJobName });
const joinListNames = joinList.map(j => j.name);

nextBuild = await remoteJoin.execute(
externalEvent,
nextJobName,
nextJobId,
nextJob,
parentBuilds,
groupEventBuilds,
joinListNames,
Expand All @@ -291,13 +287,11 @@ async function triggerNextJobs(config, app) {
}

if (isNextJobVirtual && nextBuild.status === Status.SUCCESS) {
const nextJobModel = await nextBuild.job;

downstreamOfNextJobsToBeProcessed.push({
build: nextBuild,
event: currentEvent,
job: nextJobModel,
pipeline: await nextJobModel.pipeline,
job: nextJob,
pipeline: await nextJob.pipeline,
scmContext: config.scmContext,
username: config.username
});
Expand Down
14 changes: 6 additions & 8 deletions plugins/builds/triggers/and.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,28 @@ class AndTrigger extends JoinBase {

/**
* Trigger the next jobs of the current job
* @param {String} nextJobName
* @param {String} nextJobId
* @param {Job} nextJob
* @param {Record<String, Object>} parentBuilds
* @param {String[]} joinListNames
* @param {Boolean} isNextJobVirtual
* @param {String} nextJobStageName
* @returns {Promise<Build>}
*/
async execute(nextJobName, nextJobId, parentBuilds, joinListNames, isNextJobVirtual, nextJobStageName) {
async execute(nextJob, parentBuilds, joinListNames, isNextJobVirtual, nextJobStageName) {
logger.info(`Fetching finished builds for event ${this.currentEvent.id}`);

const relatedBuilds = await this.fetchRelatedBuilds();

// Find the next build from the related builds for this event
let nextBuild = relatedBuilds.find(b => b.jobId === nextJobId && b.eventId === this.currentEvent.id);
let nextBuild = relatedBuilds.find(b => b.jobId === nextJob.id && b.eventId === this.currentEvent.id);

if (!nextBuild) {
// If the build to join fails and it succeeds on restart, depending on the timing, the latest build will be that of a child event.
// In that case, `nextBuild` will be null and will not be triggered even though there is a build that should be triggered.
// Now we need to check for the existence of a build that should be triggered in its own event.
nextBuild = await this.buildFactory.get({
eventId: this.currentEvent.id,
jobId: nextJobId
jobId: nextJob.id
});

if (nextBuild) {
Expand All @@ -84,7 +83,7 @@ class AndTrigger extends JoinBase {

if (!nextBuild) {
// If the build to join is in the child event, its event id is greater than current event.
nextBuild = relatedBuilds.find(b => b.jobId === nextJobId && b.eventId > this.currentEvent.id);
nextBuild = relatedBuilds.find(b => b.jobId === nextJob.id && b.eventId > this.currentEvent.id);
}
const newParentBuilds = mergeParentBuilds(parentBuilds, relatedBuilds, this.currentEvent, undefined);
let nextEvent = this.currentEvent;
Expand All @@ -97,8 +96,7 @@ class AndTrigger extends JoinBase {
pipelineId: this.pipelineId,
event: nextEvent,
nextBuild,
nextJobName,
nextJobId,
nextJob,
parentBuilds: newParentBuilds,
parentBuildId: this.currentBuild.id,
joinListNames,
Expand Down
24 changes: 12 additions & 12 deletions plugins/builds/triggers/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -599,13 +599,13 @@ async function getParentBuildStatus({ newBuild, joinListNames, pipelineId, build
* @param {Boolean} arg.done If the build is done or not
* @param {Boolean} arg.hasFailure If the build has a failure or not
* @param {Build} arg.newBuild Next build
* @param {String|undefined} arg.jobName Job name
* @param {Job} arg.job Next job
* @param {String|undefined} arg.pipelineId Pipeline ID
* @param {String|undefined} arg.stageName Stage name
* @param {Boolean} arg.isVirtualJob If the job is virtual or not
* @returns {Promise<Build|null>} The newly updated/created build
*/
async function handleNewBuild({ done, hasFailure, newBuild, jobName, pipelineId, stageName, isVirtualJob }) {
async function handleNewBuild({ done, hasFailure, newBuild, job, pipelineId, stageName, isVirtualJob }) {
if (!done || Status.isStarted(newBuild.status)) {
return null;
}
Expand All @@ -615,9 +615,9 @@ async function handleNewBuild({ done, hasFailure, newBuild, jobName, pipelineId,
const stageTeardownName = stageName ? getFullStageJobName({ stageName, jobName: 'teardown' }) : '';

// New build is not stage teardown job
if (jobName !== stageTeardownName) {
if (job.name !== stageTeardownName) {
logger.info(
`Failure occurred in upstream job, removing new build - build:${newBuild.id} pipeline:${pipelineId}-${jobName} event:${newBuild.eventId} `
`Failure occurred in upstream job, removing new build - build:${newBuild.id} pipeline:${pipelineId}-${job.name} event:${newBuild.eventId} `
);
await newBuild.remove();
}
Expand All @@ -626,7 +626,9 @@ async function handleNewBuild({ done, hasFailure, newBuild, jobName, pipelineId,
}

// Bypass execution of the build if the job is virtual
if (isVirtualJob) {
const hasFreezeWindows = job.permutations[0].freezeWindows && job.permutations[0].freezeWindows.length > 0;

if (isVirtualJob && !hasFreezeWindows) {
newBuild.status = Status.SUCCESS;

return newBuild.update();
Expand Down Expand Up @@ -1035,19 +1037,17 @@ function extractExternalJoinData(joinedPipelines, currentPipelineId) {
}

/**
* Get job id from job name
* Get job from job name
* @param {String} jobName Job name
* @param {String} pipelineId Pipeline id
* @param {JobFactory} jobFactory Job factory
* @returns {Promise<Number>}
* @returns {Promise<Job>}
*/
async function getJobId(jobName, pipelineId, jobFactory) {
const job = await jobFactory.get({
async function getJob(jobName, pipelineId, jobFactory) {
return jobFactory.get({
name: jobName,
pipelineId
});

return job.id;
}

/**
Expand Down Expand Up @@ -1152,7 +1152,7 @@ module.exports = {
strToInt,
createEvent,
deleteBuild,
getJobId,
getJob,
isOrTrigger,
extractCurrentPipelineJoinData,
extractExternalJoinData,
Expand Down
14 changes: 6 additions & 8 deletions plugins/builds/triggers/joinBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ class JoinBase {
* @param {Number} pipelineId
* @param {Event} event
* @param {Build} nextBuild
* @param {String} nextJobName
* @param {String} nextJobId
* @param {Job} nextJob
* @param {import('./helpers').ParentBuilds} parentBuilds
* @param {String} parentBuildId
* @param {String[]} joinListNames
Expand All @@ -52,8 +51,7 @@ class JoinBase {
pipelineId,
event,
nextBuild,
nextJobName,
nextJobId,
nextJob,
parentBuilds,
parentBuildId,
joinListNames,
Expand All @@ -68,8 +66,8 @@ class JoinBase {
jobFactory: this.jobFactory,
buildFactory: this.buildFactory,
pipelineId,
jobName: nextJobName,
jobId: nextJobId,
jobName: nextJob.name,
jobId: nextJob.id,
username: this.username,
scmContext: this.scmContext,
event, // this is the parentBuild for the next build
Expand All @@ -87,7 +85,7 @@ class JoinBase {
}

if (!newBuild) {
logger.error(`No build found for ${pipelineId}:${nextJobName}`);
logger.error(`No build found for ${pipelineId}:${nextJob.name}`);

return null;
}
Expand All @@ -104,7 +102,7 @@ class JoinBase {
done,
hasFailure,
newBuild,
jobName: nextJobName,
job: nextJob,
pipelineId,
isVirtualJob: isNextJobVirtual,
stageName: nextJobStageName
Expand Down
7 changes: 3 additions & 4 deletions plugins/builds/triggers/or.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@ class OrTrigger extends OrBase {
* Trigger the next jobs of the current job
* @param {Event} event
* @param {Number} pipelineId
* @param {String} nextJobName
* @param {Number} nextJobId
* @param {Job} nextJob
* @param {import('./helpers').ParentBuilds} parentBuilds
* @param {Boolean} isNextJobVirtual
* @return {Promise<Build|null>}
*/
async execute(event, pipelineId, nextJobName, nextJobId, parentBuilds, isNextJobVirtual) {
return this.trigger(event, pipelineId, nextJobName, nextJobId, parentBuilds, isNextJobVirtual);
async execute(event, pipelineId, nextJob, parentBuilds, isNextJobVirtual) {
return this.trigger(event, pipelineId, nextJob, parentBuilds, isNextJobVirtual);
}
}

Expand Down
20 changes: 11 additions & 9 deletions plugins/builds/triggers/orBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,27 @@ class OrBase {
* Trigger the next jobs of the current job
* @param {Event} event
* @param {Number} pipelineId
* @param {String} nextJobName
* @param {Number} nextJobId
* @param {Job} nextJob
* @param {import('./helpers').ParentBuilds} parentBuilds
* @param {Boolean} isNextJobVirtual
* @return {Promise<Build|null>}
*/
async trigger(event, pipelineId, nextJobName, nextJobId, parentBuilds, isNextJobVirtual) {
async trigger(event, pipelineId, nextJob, parentBuilds, isNextJobVirtual) {
let nextBuild = await this.buildFactory.get({
eventId: event.id,
jobId: nextJobId
jobId: nextJob.id
});

const hasFreezeWindows =
nextJob.permutations[0].freezeWindows && nextJob.permutations[0].freezeWindows.length > 0;

if (nextBuild !== null) {
if (Status.isStarted(nextBuild.status)) {
return nextBuild;
}

// Bypass execution of the build if the job is virtual
if (isNextJobVirtual) {
if (isNextJobVirtual && !hasFreezeWindows) {
nextBuild.status = Status.SUCCESS;

return nextBuild.update();
Expand All @@ -70,19 +72,19 @@ class OrBase {
jobFactory: this.jobFactory,
buildFactory: this.buildFactory,
pipelineId,
jobName: nextJobName,
jobId: nextJobId,
jobName: nextJob.name,
jobId: nextJob.id,
username: this.username,
scmContext: this.scmContext,
event,
baseBranch: event.baseBranch || null,
parentBuilds,
parentBuildId: this.currentBuild.id,
start: !isNextJobVirtual
start: hasFreezeWindows || !isNextJobVirtual
});

// Bypass execution of the build if the job is virtual
if (isNextJobVirtual) {
if (isNextJobVirtual && !hasFreezeWindows) {
nextBuild.status = Status.SUCCESS;

nextBuild = nextBuild.update();
Expand Down
11 changes: 4 additions & 7 deletions plugins/builds/triggers/remoteJoin.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ class RemoteJoin extends JoinBase {
/**
* Trigger the next external jobs of the current job
* @param {Event} externalEvent Downstream pipeline's event
* @param {String} nextJobName
* @param {Number} nextJobId
* @param {Job} nextJob
* @param {import('./helpers').ParentBuilds} parentBuilds
* @param {Build[]} groupEventBuilds Builds of the downstream pipeline, where only the latest ones for each job are included that have the same groupEventId as the externalEvent
* @param {String[]} joinListNames
Expand All @@ -34,16 +33,15 @@ class RemoteJoin extends JoinBase {
*/
async execute(
externalEvent,
nextJobName,
nextJobId,
nextJob,
parentBuilds,
groupEventBuilds,
joinListNames,
isNextJobVirtual,
nextJobStageName
) {
// When restart case, should we create a new build ?
const nextBuild = groupEventBuilds.find(b => b.jobId === nextJobId && b.eventId === externalEvent.id);
const nextBuild = groupEventBuilds.find(b => b.jobId === nextJob.id && b.eventId === externalEvent.id);

const newParentBuilds = mergeParentBuilds(parentBuilds, groupEventBuilds, this.currentEvent, externalEvent);

Expand All @@ -58,8 +56,7 @@ class RemoteJoin extends JoinBase {
pipelineId: externalEvent.pipelineId,
event: externalEvent,
nextBuild,
nextJobName,
nextJobId,
nextJob,
parentBuilds: newParentBuilds,
parentBuildId,
joinListNames,
Expand Down
Loading