/** Immediately kills the build. */ @RequirePOST public void doKill() { checkPermission(Item.CANCEL); if (!isBuilding() || /* probably redundant, but just to be sure */ execution == null) { return; } if (listener != null) { listener.getLogger().println("Hard kill!"); } execution = null; // ensures isInProgress returns false FlowInterruptedException suddenDeath = new FlowInterruptedException(Result.ABORTED); finish(Result.ABORTED, suddenDeath); executionPromise.setException(suddenDeath); // TODO CpsFlowExecution.onProgramEnd does some cleanup which we cannot access here; perhaps need a FlowExecution.halt(Throwable) API? }
@Override public void onFailure(StepContext context, Throwable t) { try { TaskListener listener = context.get(TaskListener.class); Result r = Result.FAILURE; if (t instanceof AbortException) { listener.error(t.getMessage()); } else if (t instanceof FlowInterruptedException) { FlowInterruptedException fie = (FlowInterruptedException) t; fie.handle(context.get(Run.class), listener); r = fie.getResult(); } else { listener.getLogger().println(Functions.printThrowable(t).trim()); // TODO 2.43+ use Functions.printStackTrace } context.get(Run.class).setResult(r); context.onSuccess(null); } catch (Exception x) { context.onFailure(x); } }
/** * If a build catches this exception, it should use this method to report it. * @param run * @param listener */ public void handle(Run<?,?> run, TaskListener listener) { Set<CauseOfInterruption> boundCauses = new HashSet<>(); for (InterruptedBuildAction a : run.getActions(InterruptedBuildAction.class)) { boundCauses.addAll(a.getCauses()); } Collection<CauseOfInterruption> diff = Sets.difference(new LinkedHashSet<>(causes), boundCauses); if (!diff.isEmpty()) { run.addAction(new InterruptedBuildAction(diff)); for (CauseOfInterruption cause : diff) { cause.print(listener); } } print(getCause(), listener); for (Throwable t : getSuppressed()) { print(t, listener); } } private static void print(@CheckForNull Throwable t, @Nonnull TaskListener listener) {
FlowInterruptedException cause = new FlowInterruptedException(Result.FAILURE, new BodyFailed()); cause.initCause(getOutcome().getAbnormal()); try {
listener.error(t.getMessage()); } else if (t instanceof FlowInterruptedException) { ((FlowInterruptedException) t).handle(this, listener); } else if (t != null) {
Throwable error = o.getAbnormal(); if (error instanceof FlowInterruptedException) { result = ((FlowInterruptedException) error).getResult();
Throwable failure = newOutcome.getAbnormal(); if (failure instanceof FlowInterruptedException) { for (CauseOfInterruption cause : ((FlowInterruptedException) failure).getCauses()) { if (cause instanceof BodyFailed) { LOGGER.log(Level.FINE, "already completed " + this + " and now received body failure", failure);
final FlowInterruptedException ex = new FlowInterruptedException(result,causes);
/** * If a build catches this exception, it should use this method to report it. * @param run * @param listener */ public void handle(Run<?,?> run, TaskListener listener) { Set<CauseOfInterruption> boundCauses = new HashSet<>(); for (InterruptedBuildAction a : run.getActions(InterruptedBuildAction.class)) { boundCauses.addAll(a.getCauses()); } Collection<CauseOfInterruption> diff = Sets.difference(new LinkedHashSet<>(causes), boundCauses); if (!diff.isEmpty()) { run.addAction(new InterruptedBuildAction(diff)); for (CauseOfInterruption cause : diff) { cause.print(listener); } } print(getCause(), listener); for (Throwable t : getSuppressed()) { print(t, listener); } } private static void print(@CheckForNull Throwable t, @Nonnull TaskListener listener) {
if (isDone()) return false; // already complete stopped = new FlowInterruptedException(Result.ABORTED, causes); t = this.thread;
final Throwable x = new FlowInterruptedException(Result.ABORTED); Futures.addCallback(this.getCurrentExecutions(/* cf. JENKINS-26148 */true), new FutureCallback<List<StepExecution>>() { @Override public void onSuccess(List<StepExecution> l) {
/** Sends {@link StepContext#onFailure} to all running (leaf) steps. */ @RequirePOST public void doTerm() { checkPermission(Item.CANCEL); if (!isInProgress() || /* redundant, but make FindBugs happy */ execution == null) { return; } final Throwable x = new FlowInterruptedException(Result.ABORTED); Futures.addCallback(execution.getCurrentExecutions(/* cf. JENKINS-26148 */true), new FutureCallback<List<StepExecution>>() { @Override public void onSuccess(List<StepExecution> l) { for (StepExecution e : Iterators.reverse(l)) { StepContext context = e.getContext(); context.onFailure(x); try { FlowNode n = context.get(FlowNode.class); if (n != null) { listener.getLogger().println("Terminating " + n.getDisplayFunctionName()); } } catch (Exception x) { LOGGER.log(Level.FINE, null, x); } } } @Override public void onFailure(Throwable t) {} }); printLater(StopState.KILL, "Click here to forcibly kill entire build"); }
return; final Throwable death = new FlowInterruptedException(Result.ABORTED, new ExceededTimeout());
private static void cancel(StepContext context, StepContext newer) throws IOException, InterruptedException { if (context.isReady() && newer.isReady()) { println(context, "Canceled since " + newer.get(Run.class).getDisplayName() + " got here"); println(newer, "Canceling older " + context.get(Run.class).getDisplayName()); context.onFailure(new FlowInterruptedException(Result.NOT_BUILT, new CanceledCause(newer.get(Run.class)))); } else { LOGGER.log(WARNING, "cannot cancel dead {0} or {1}", new Object[] {context, newer}); } }
/** * REST endpoint to abort the workflow. */ @RequirePOST public HttpResponse doAbort() { preAbortCheck(); FlowInterruptedException e = new FlowInterruptedException(Result.ABORTED, new Rejection(User.current())); outcome = new Outcome(null,e); postSettlement(); getContext().onFailure(e); // TODO: record this decision to FlowNode return HttpResponses.ok(); }