Skip to content

Commit

Permalink
Merge pull request #483 from ckreisl/feature-abort-abandoned-patchsets
Browse files Browse the repository at this point in the history
[JENKINS-49777] Abort running build if Gerrit patchset is abandoned
  • Loading branch information
rsandell committed Feb 24, 2023
2 parents 288f5dc + c956707 commit c778a14
Show file tree
Hide file tree
Showing 11 changed files with 300 additions and 55 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* The MIT License
*
* Copyright 2022 Christoph Kreisl. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger;

import com.sonyericsson.hudson.plugins.gerrit.trigger.Messages;
import jenkins.model.CauseOfInterruption;

/**
* A cause class for new build interruption.
* @author ckreisl
*/
public final class AbandonedPatchsetInterruption extends CauseOfInterruption {
private static final long serialVersionUID = 1L;

@Override
public String getShortDescription() {
return Messages.AbortedByAbandonedPatchset();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ public void gerritEvent(GerritEvent event) {
GerritTriggeredEvent triggeredEvent = (GerritTriggeredEvent)event;
if (t.isInteresting(triggeredEvent)) {
logger.trace("The event is interesting.");
abortBuild(t, triggeredEvent);
if (t.isOnlyAbortRunningBuild(triggeredEvent)) {
logger.trace("Just aborting build based on event not scheduling new one.");
return;
}
notifyOnTriggered(t, triggeredEvent);
schedule(t, new GerritCause(triggeredEvent, t.isSilentMode()), triggeredEvent);
}
Expand Down Expand Up @@ -159,6 +164,11 @@ public void gerritEvent(ManualPatchsetCreated event) {
}
if (t.isInteresting(event)) {
logger.trace("The event is interesting.");
abortBuild(t, event);
if (t.isOnlyAbortRunningBuild(event)) {
logger.trace("Just aborting build based on event not scheduling new one.");
return;
}
notifyOnTriggered(t, event);
schedule(t, new GerritManualCause(event, t.isSilentMode()), event);
}
Expand Down Expand Up @@ -198,11 +208,39 @@ public void gerritEvent(CommentAdded event) {
}
if (t.isInteresting(event) && t.commentAddedMatch(event)) {
logger.trace("The event is interesting.");
abortBuild(t, event);
if (t.isOnlyAbortRunningBuild(event)) {
logger.trace("Just aborting build based on event not scheduling new one.");
return;
}
notifyOnTriggered(t, event);
schedule(t, new GerritCause(event, t.isSilentMode()), event);
}
}

/**
* Abort running builds based on the BuildCancellationPolicy and event.
*
* @param t GerritTrigger class.
* @param event GerritTriggeredEvent.
*/
private void abortBuild(GerritTrigger t, GerritTriggeredEvent event) {
if (!(event instanceof ChangeBasedEvent)) {
return;
}

ChangeBasedEvent changeBasedEvent = (ChangeBasedEvent)event;
if (t.getBuildCancellationPolicy() != null && t.getBuildCancellationPolicy().isEnabled()) {
t.getRunningJobs(t.getJob()).cancelTriggeredJob(changeBasedEvent,
t.getJob().getFullName(), t.getBuildCancellationPolicy());
}

IGerritHudsonTriggerConfig serverConfig = getServerConfig(event);
if (serverConfig != null && (serverConfig.isGerritBuildCurrentPatchesOnly())) {
t.getRunningJobs(t.getJob()).scheduled(changeBasedEvent);
}
}

/**
* Schedules a build with parameters from the event. With {@link #job} as the project to build.
*
Expand Down Expand Up @@ -245,18 +283,8 @@ protected void schedule(GerritTrigger t, GerritCause cause, GerritTriggeredEvent
+ project.getClass().getName());
}

IGerritHudsonTriggerConfig serverConfig = getServerConfig(event);

if (event instanceof ChangeBasedEvent) {
ChangeBasedEvent changeBasedEvent = (ChangeBasedEvent)event;
if (t.getBuildCancellationPolicy() != null && t.getBuildCancellationPolicy().isEnabled())
{
t.getRunningJobs(project).cancelTriggeredJob(changeBasedEvent,
t.getJob().getFullName(), t.getBuildCancellationPolicy());
}
if (serverConfig != null && (serverConfig.isGerritBuildCurrentPatchesOnly())) {
t.getRunningJobs(project).scheduled(changeBasedEvent);
}
if (null != changeBasedEvent.getPatchSet()) {
logger.info("Project {} Build Scheduled: {} By event: {}",
project.getName(), (futureBuild != null),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.events.PluginDraftPublishedEvent;
import com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.events.PluginGerritEvent;
import com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.events.PluginPatchsetCreatedEvent;
import com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.events.PluginChangeAbandonedEvent;
import com.sonyericsson.hudson.plugins.gerrit.trigger.version.GerritVersionChecker;
import com.sonymobile.tools.gerrit.gerritevents.GerritHandler;
import com.sonymobile.tools.gerrit.gerritevents.GerritQueryHandler;
Expand All @@ -54,6 +55,7 @@
import com.sonymobile.tools.gerrit.gerritevents.dto.events.ChangeBasedEvent;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.WipStateChanged;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.CommentAdded;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.ChangeAbandoned;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.GerritTriggeredEvent;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.RefUpdated;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.TopicChanged;
Expand Down Expand Up @@ -666,6 +668,21 @@ private boolean shouldTriggerOnEventType(GerritTriggeredEvent event) {
return false;
}

if (event instanceof ChangeAbandoned) {
if (buildCancellationPolicy != null && buildCancellationPolicy.isEnabled()) {
if (buildCancellationPolicy.isAbortAbandonedPatchsets()) {
return true;
}
}

IGerritHudsonTriggerConfig config = getServerConfig(event);
if (config != null && config.getBuildCurrentPatchesOnly().isEnabled()) {
if (config.getBuildCurrentPatchesOnly().isAbortAbandonedPatchsets()) {
return true;
}
}
}

for (PluginGerritEvent e : triggerOnEvents) {
if (!e.shouldTriggerOn(event)) {
continue;
Expand Down Expand Up @@ -1062,6 +1079,45 @@ private boolean isTopicAssociationInteresting(ChangeBasedEvent event, GerritProj
return isTopicInteresting(topic, project, event);
}

/**
* Checks based on the current event if the job should just be aborted,
* or even a new job should be triggered.
*
* @param event The ChangeBasedEvent.
* @return true if the job should only be aborted without triggering a new one, otherwise false.
*/
public boolean isOnlyAbortRunningBuild(GerritTriggeredEvent event) {

if (!(event instanceof ChangeBasedEvent)) {
return false;
}

if (!(event instanceof ChangeAbandoned)) {
return false;
}

for (PluginGerritEvent e : triggerOnEvents) {
if (e instanceof PluginChangeAbandonedEvent) {
return false;
}
}

if (buildCancellationPolicy != null && buildCancellationPolicy.isEnabled()) {
if (buildCancellationPolicy.isAbortAbandonedPatchsets()) {
return true;
}
}

IGerritHudsonTriggerConfig serverConfig = getServerConfig(event);
if (serverConfig != null && serverConfig.isGerritBuildCurrentPatchesOnly()) {
if (serverConfig.getBuildCurrentPatchesOnly().isAbortAbandonedPatchsets()) {
return true;
}
}

return false;
}

/**
* Should we trigger on this event?
*
Expand Down Expand Up @@ -1090,11 +1146,11 @@ public boolean isInteresting(GerritTriggeredEvent event) {
}
}

if (!shouldTriggerOnEventType(event)) {
if (!isServerInteresting(event)) {
return false;
}

if (!isServerInteresting(event)) {
if (!shouldTriggerOnEventType(event)) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import com.sonyericsson.hudson.plugins.gerrit.trigger.config.IGerritHudsonTriggerConfig;
import com.sonyericsson.hudson.plugins.gerrit.trigger.events.ManualPatchsetCreated;
import com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.data.BuildCancellationPolicy;
import com.sonymobile.tools.gerrit.gerritevents.dto.attr.Change;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.ChangeAbandoned;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.ChangeBasedEvent;
import com.sonymobile.tools.gerrit.gerritevents.dto.events.GerritTriggeredEvent;
import hudson.model.Cause;
Expand All @@ -21,6 +23,8 @@
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import jenkins.model.CauseOfInterruption;
import jenkins.model.Jenkins;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -73,10 +77,14 @@ public void setJob(Item job) {
*/
public void cancelTriggeredJob(ChangeBasedEvent event, String jobName, BuildCancellationPolicy policy)
{
if (policy == null || !policy.isEnabled() && (event instanceof ManualPatchsetCreated
&& !policy.isAbortManualPatchsets())) {
if (policy == null || !policy.isEnabled()) {
return;
}

if ((event instanceof ManualPatchsetCreated && !policy.isAbortManualPatchsets())) {
return;
}

this.cancelOutDatedEvents(event, policy, jobName);
}

Expand All @@ -95,7 +103,6 @@ public void scheduled(ChangeBasedEvent event) {
}

BuildCancellationPolicy serverBuildCurrentPatchesOnly = serverConfig.getBuildCurrentPatchesOnly();

if (!serverBuildCurrentPatchesOnly.isEnabled()
|| (event instanceof ManualPatchsetCreated
&& !serverBuildCurrentPatchesOnly.isAbortManualPatchsets())) {
Expand All @@ -115,6 +122,8 @@ public void scheduled(ChangeBasedEvent event) {
private void cancelOutDatedEvents(ChangeBasedEvent event, BuildCancellationPolicy policy, String jobName)
{
List<ChangeBasedEvent> outdatedEvents = new ArrayList<>();
CauseOfInterruption cause = new NewPatchSetInterruption();

synchronized (runningJobs) {
Iterator<GerritTriggeredEvent> it = runningJobs.iterator();
while (it.hasNext()) {
Expand All @@ -131,17 +140,22 @@ private void cancelOutDatedEvents(ChangeBasedEvent event, BuildCancellationPolic
outdatedEvents.add(runningChangeBasedEvent);
it.remove();
}

// add our new job
if (!outdatedEvents.contains(event)) {
runningJobs.add(event);
if (trigger.isOnlyAbortRunningBuild(event)) {
cause = new AbandonedPatchsetInterruption();
} else {
runningJobs.add(event);
}
}
}

// This step can't be done under the lock, because cancelling the jobs needs a lock on higher level.
for (ChangeBasedEvent outdatedEvent : outdatedEvents) {
logger.debug("Cancelling build for " + outdatedEvent);
try {
cancelMatchingJobs(outdatedEvent, jobName);
cancelMatchingJobs(outdatedEvent, jobName, cause);
} catch (Exception e) {
// Ignore any problems with canceling the job.
logger.error("Error canceling job", e);
Expand All @@ -163,17 +177,15 @@ private boolean shouldIgnoreEvent(ChangeBasedEvent event,
// Find all entries in runningJobs with the same Change #.
// Optionally, ignore all manual patchsets and don't cancel builds due to
// a retrigger of an older build.
boolean abortBecauseOfTopic = trigger.abortBecauseOfTopic(event,
policy,
runningChangeBasedEvent);
boolean abortBecauseOfTopic = trigger.abortBecauseOfTopic(event, policy, runningChangeBasedEvent);

if (!abortBecauseOfTopic && !runningChangeBasedEvent.getChange().equals(event.getChange())) {
Change change = runningChangeBasedEvent.getChange();
if (!abortBecauseOfTopic && !change.equals(event.getChange())) {
return true;
}

boolean shouldCancelManual = (runningChangeBasedEvent instanceof ManualPatchsetCreated
&& policy.isAbortManualPatchsets()
|| !(runningChangeBasedEvent instanceof ManualPatchsetCreated));
boolean shouldCancelManual = (!(runningChangeBasedEvent instanceof ManualPatchsetCreated)
|| policy.isAbortManualPatchsets());

if (!abortBecauseOfTopic && !shouldCancelManual) {
return true;
Expand All @@ -183,7 +195,10 @@ private boolean shouldIgnoreEvent(ChangeBasedEvent event,
|| Integer.parseInt(runningChangeBasedEvent.getPatchSet().getNumber())
< Integer.parseInt(event.getPatchSet().getNumber());

if (!abortBecauseOfTopic && !shouldCancelPatchsetNumber) {
boolean isAbortAbandonedPatchset = policy.isAbortAbandonedPatchsets()
&& (event instanceof ChangeAbandoned);

if (!abortBecauseOfTopic && !shouldCancelPatchsetNumber && !isAbortAbandonedPatchset) {
return true;
}

Expand All @@ -205,8 +220,9 @@ private boolean shouldIgnoreEvent(ChangeBasedEvent event,
*
* @param event The event that originally triggered the build.
* @param jobName job name to match on.
* @param cause The cause of the build interruption.
*/
private void cancelMatchingJobs(GerritTriggeredEvent event, String jobName) {
private void cancelMatchingJobs(GerritTriggeredEvent event, String jobName, CauseOfInterruption cause) {
try {
if (!(this.job instanceof Queue.Task)) {
logger.error("Error canceling job. The job is not of type Task. Job name: " + getJob().getName());
Expand Down Expand Up @@ -242,7 +258,7 @@ private void cancelMatchingJobs(GerritTriggeredEvent event, String jobName) {
continue;
}

e.interrupt(Result.ABORTED, new NewPatchSetInterruption());
e.interrupt(Result.ABORTED, cause);
}
}
} catch (Exception e) {
Expand Down Expand Up @@ -272,6 +288,15 @@ private boolean checkCausedByGerrit(GerritTriggeredEvent event, Collection<Cause
return false;
}

/**
* Adds the event to the running jobs.
*
* @param event The ChangeBasedEvent.
*/
public void add(ChangeBasedEvent event) {
runningJobs.add(event);
}

/**
* Removes any reference to the current build for this change.
*
Expand Down

0 comments on commit c778a14

Please sign in to comment.