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

feat(3181): Skip execution of a virtual job when an event is started from that job #3252

Merged
merged 2 commits into from
Dec 16, 2024

Conversation

sagar1312
Copy link
Member

Context

Per changes made in screwdriver-cd/models#635, virtual job builds at the beginning in the workflow for an event, may not be queued for execution. Status of such build will be set to CREATED.

Objective

Identify such build immediately after creating the event to mark its status as 'SUCCESS' and trigger its downstream jobs.

References

#3181

License

I confirm that this contribution is made under a BSD license and that I have the authority necessary to make this contribution on behalf of its copyright owner.

@coveralls
Copy link

coveralls commented Dec 12, 2024

Coverage Status

coverage: 95.306% (+0.1%) from 95.173%
when pulling 409534a on sagar1312-feat-3181-skip-virtual-job-as-event-origin
into f71ebdf on master.

});
} else if (desiredStatus === 'QUEUED' && currentStatus !== 'QUEUED') {
throw boom.badRequest(`Cannot update builds to ${desiredStatus}`);
} else if (desiredStatus === 'BLOCKED' && currentStatus === 'BLOCKED') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

making sure that currentStatus === 'BLOCKED' is what needed here and not currentStatus !== 'BLOCKED'.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an existing code. Just moved it to a common module. Avoiding any additional refactoring/correction as part of this PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No change needed. This is an exiting logic. Just moved it to a common module.
Per the comment "Queue-Service can call BLOCKED status update multiple times", both desiredStatus and currentStatus can be BLOCKED.

@@ -244,6 +246,23 @@ module.exports = () => ({
if (event.builds === null) {
return boom.notFound('No jobs to start.');
}

const virtualJobBuilds = event.builds.filter(b => b.status === 'CREATED');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is that enough to assume that all builds with status === 'CREATED' is a virtual job then?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, shouldn't this be virtualJobBuild (i.e., singular) as the event is triggered from a single job and there should only be 1 build created for the virtual job?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be plural, when we have an event started from ~pr or ~commit, and there are more than one virtual job listening to these triggers.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, builds created immediately after the event is created would be in CREATED status only if the job is virtual.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about builds that are waiting for other joined parent downstream builds to complete? Aren't those builds normally created and then park with "CREATED" status?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are the builds at the beginning of the workflow for an event. I believe, these wouldn't be waiting for any upstream.

plugins/builds/helper/updateBuild.js Show resolved Hide resolved
Comment on lines 53 to 58
/**
* Updates execution details for init step
* @method stopFrozenBuild
* @param {Object} build Build Object
* @param {Object} app Hapi app Object
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/**
* Updates execution details for init step
* @method stopFrozenBuild
* @param {Object} build Build Object
* @param {Object} app Hapi app Object
*/
/**
* Updates execution details for init step
* @method updateInitStep
* @param {Object} build Build Object
* @param {Object} app Hapi app Object
* @return {Promise} Promise resolving when the init step is updated
*/

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

step.endTime = build.startTime || new Date().toISOString();
step.code = 0;

return step.update();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return step.update();
return await step.update();

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 86 to 105
if (currentStatus !== 'UNSTABLE') {
if (desiredStatus !== undefined) {
build.status = desiredStatus;
}
if (build.status === 'ABORTED') {
if (currentStatus === 'FROZEN') {
build.statusMessage = `Frozen build aborted by ${username}`;
} else {
build.statusMessage = `Aborted by ${username}`;
}
} else if (build.status === 'FAILURE' || build.status === 'SUCCESS') {
if (statusMessage) {
build.statusMessage = statusMessage;
build.statusMessageType = statusMessageType || null;
}
} else {
build.statusMessage = statusMessage || null;
build.statusMessageType = statusMessageType || null;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (currentStatus !== 'UNSTABLE') {
if (desiredStatus !== undefined) {
build.status = desiredStatus;
}
if (build.status === 'ABORTED') {
if (currentStatus === 'FROZEN') {
build.statusMessage = `Frozen build aborted by ${username}`;
} else {
build.statusMessage = `Aborted by ${username}`;
}
} else if (build.status === 'FAILURE' || build.status === 'SUCCESS') {
if (statusMessage) {
build.statusMessage = statusMessage;
build.statusMessageType = statusMessageType || null;
}
} else {
build.statusMessage = statusMessage || null;
build.statusMessageType = statusMessageType || null;
}
}
// UNSTABLE -> SUCCESS needs to update meta and endtime.
// However, the status itself cannot be updated to SUCCESS
if (currentStatus === 'UNSTABLE') {
return;
}
// Update the build status if desiredStatus is provided
if (desiredStatus !== undefined) {
build.status = desiredStatus;
}
// Handle different statuses and set the appropriate status message
switch (build.status) {
case 'ABORTED':
build.statusMessage = currentStatus === 'FROZEN'
? `Frozen build aborted by ${username}`
: `Aborted by ${username}`;
break;
case 'FAILURE':
case 'SUCCESS':
if (statusMessage) {
build.statusMessage = statusMessage;
build.statusMessageType = statusMessageType || null;
}
break;
default:
build.statusMessage = statusMessage || null;
build.statusMessageType = statusMessageType || null;
break;
}

to improve readability:

  • use early return
  • use switch instead of nested if else.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 138 to 140
const stageJobIds = stage.jobIds;

stageJobIds.push(stage.setup);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const stageJobIds = stage.jobIds;
stageJobIds.push(stage.setup);
const stageJobIds = [...stage.jobIds, stage.setup];

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

eventFactoryMock.get.withArgs(parentEventId).resolves(getEventMock(testEvent));
eventFactoryMock.create.resolves(getEventMock(testEvent));
eventMock = getEventMock(testEvent);
// eventFactoryMock.get.withArgs(parentEventId).resolves(getEventMock(testEvent));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// eventFactoryMock.get.withArgs(parentEventId).resolves(getEventMock(testEvent));

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -244,6 +246,23 @@ module.exports = () => ({
if (event.builds === null) {
return boom.notFound('No jobs to start.');
}

const virtualJobBuilds = event.builds.filter(b => b.status === 'CREATED');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, shouldn't this be virtualJobBuild (i.e., singular) as the event is triggered from a single job and there should only be 1 build created for the virtual job?

@sagar1312
Copy link
Member Author

@minghay @VonnyJap I have addressed the suggestions. Please take a look.

@VonnyJap VonnyJap merged commit 36d3365 into master Dec 16, 2024
2 checks passed
@VonnyJap VonnyJap deleted the sagar1312-feat-3181-skip-virtual-job-as-event-origin branch December 16, 2024 18:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants