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

[JENKINS-49707] Introduce ErrorCondition #217

Merged
merged 10 commits into from
Jul 7, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,13 @@
import org.codehaus.groovy.control.MultipleCompilationErrorsException;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Functions;
import jenkins.model.Jenkins;
import org.apache.commons.io.output.NullOutputStream;
import org.jenkinsci.plugins.workflow.flow.FlowExecution;
import org.jenkinsci.plugins.workflow.graph.AtomNode;
import org.jenkinsci.plugins.workflow.graph.BlockEndNode;
import org.jenkinsci.plugins.workflow.graphanalysis.ForkScanner;

/**
* Attached to {@link FlowNode} that caused an error.
Expand Down Expand Up @@ -111,4 +116,41 @@ public String getDisplayName() {
public String getUrlName() {
return null;
}

/**
* Attempts to locate the first node of a build which threw an error.
* Typically an error will be rethrown by enclosing blocks,
* so this will look for the original node with an {@link ErrorAction}
* matching the given stack trace.
* @param error an error thrown at some point during a build
* @param execution the build
* @return the originating node, if one can be located;
* typically an {@link AtomNode} or {@link BlockEndNode}
* (in the latter case you may want to use {@link BlockEndNode#getStartNode} to look up actions)
*/
public static @CheckForNull FlowNode findOrigin(@NonNull Throwable error, @NonNull FlowExecution execution) {
FlowNode candidate = null;
for (FlowNode n : new ForkScanner().allNodes(execution)) {
ErrorAction errorAction = n.getPersistentAction(ErrorAction.class);
if (errorAction != null && equals(error, errorAction.getError())) {
candidate = n; // continue search for earlier one
}
}
return candidate;
}

/**
* {@link Throwable#equals} might not be reliable if the program has resumed
* and stuff is deserialized.
*/
private static boolean equals(Throwable t1, Throwable t2) {
if (t1 == t2) {
return true;
} else if (t1.getClass() != t2.getClass()) {
return false;
} else {
return Functions.printThrowable(t1).equals(Functions.printThrowable(t2));
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* The MIT License
*
* Copyright 2022 CloudBees, Inc.
*
* 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 org.jenkinsci.plugins.workflow.flow;

import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.ExtensionPoint;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
import java.io.IOException;
import java.io.Serializable;
import org.jenkinsci.plugins.workflow.actions.ErrorAction;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.Beta;

/**
* User-configurable predicate for errors that may occur during a build.
* Implementations could check for agent-related outages, for example.
* Step callers could use a condition to decide whether to ignore or report an error, retry, etc.
*/
@Restricted(Beta.class)
public abstract class ErrorCondition extends AbstractDescribableImpl<ErrorCondition> implements ExtensionPoint, Serializable {

/**
* Checks whether a given error matches this condition.
* @param error some exception thrown during a build
* @param context the context in which the error is being <em>handled</em>, if available;
* {@link ErrorAction#findOrigin} could be used to determine the part of the build in which the error was <em>thrown</em>
* @return true if the error is recognized
* @throws IOException as per {@link StepContext#get}
* @throws InterruptedException as per {@link StepContext#get}
*/
public abstract boolean test(@NonNull Throwable error, @CheckForNull StepContext context) throws IOException, InterruptedException;

@Override public ErrorConditionDescriptor getDescriptor() {
return (ErrorConditionDescriptor) super.getDescriptor();
}

public static abstract class ErrorConditionDescriptor extends Descriptor<ErrorCondition> {}

}