use of org.jenkinsci.plugins.workflow.graph.FlowEndNode in project workflow-cps-plugin by jenkinsci.
the class CpsFlowExecution method createPlaceholderNodes.
/**
* In the event we're missing FlowNodes, fail-fast and create some mockup FlowNodes so we can continue.
* This avoids nulling out all of the execution's data
* Bypasses {@link #croak(Throwable)} and {@link #onProgramEnd(Outcome)} to guarantee a clean path.
*/
@GuardedBy("this")
void createPlaceholderNodes(Throwable failureReason) throws Exception {
synchronized (this) {
this.done = true;
if (this.owner != null) {
// Ensure that the Run is marked as completed (failed) if it isn't already so it won't show as running
Queue.Executable ex = owner.getExecutable();
if (ex instanceof Run) {
Result res = ((Run) ex).getResult();
setResult(res != null ? res : Result.FAILURE);
}
}
programPromise = Futures.immediateFailedFuture(new IllegalStateException("Failed loading heads", failureReason));
LOGGER.log(Level.INFO, "Creating placeholder flownodes for execution: " + this);
if (this.owner != null) {
try {
owner.getListener().getLogger().println("Creating placeholder flownodes because failed loading originals.");
} catch (Exception ex) {
// It's okay to fail to log
}
}
// Switch to fallback storage so we don't delete original node data
this.storageDir = (this.storageDir != null) ? this.storageDir + "-fallback" : "workflow-fallback";
// Empty storage
this.storage = createStorage();
// Clear out old start nodes and heads
this.startNodes = new Stack<>();
FlowHead head = new FlowHead(this);
this.heads = new TreeMap<>();
heads.put(head.getId(), head);
FlowStartNode start = new FlowStartNode(this, iotaStr());
head.newStartNode(start);
// Create end
FlowNode end = new FlowEndNode(this, iotaStr(), (FlowStartNode) startNodes.pop(), result, getCurrentHeads().toArray(new FlowNode[0]));
end.addAction(new ErrorAction(failureReason));
head.setNewHead(end);
}
saveOwner();
}
use of org.jenkinsci.plugins.workflow.graph.FlowEndNode in project workflow-cps-plugin by jenkinsci.
the class PersistenceProblemsTest method assertCompletedCleanly.
/**
* Verifies all the assumptions about a cleanly finished build.
*/
static void assertCompletedCleanly(WorkflowRun run) throws Exception {
while (run.isBuilding()) {
// TODO seems to be unpredictable
Thread.sleep(100);
}
Assert.assertNotNull(run.getResult());
FlowExecution fe = run.getExecution();
FlowExecutionList.get().forEach(f -> {
if (fe != null && f == fe) {
Assert.fail("FlowExecution still in FlowExecutionList!");
}
});
Assert.assertTrue("Queue not empty after completion!", Queue.getInstance().isEmpty());
if (fe instanceof CpsFlowExecution) {
CpsFlowExecution cpsExec = (CpsFlowExecution) fe;
Assert.assertTrue(cpsExec.isComplete());
Assert.assertEquals(Boolean.TRUE, cpsExec.done);
Assert.assertEquals(1, cpsExec.getCurrentHeads().size());
Assert.assertTrue(cpsExec.isComplete());
Assert.assertTrue(cpsExec.getCurrentHeads().get(0) instanceof FlowEndNode);
Assert.assertTrue(cpsExec.startNodes == null || cpsExec.startNodes.isEmpty());
while (cpsExec.blocksRestart()) {
// TODO ditto
Thread.sleep(100);
}
} else {
System.out.println("WARNING: no FlowExecutionForBuild");
}
}
use of org.jenkinsci.plugins.workflow.graph.FlowEndNode in project selenium_java by sergueik.
the class Audit2DbGraphListener method onNewHead.
@Override
public void onNewHead(FlowNode node) {
if (!FlowEndNode.class.isInstance(node)) {
return;
}
LOGGER.fine("Found the FlowEndNode.");
FlowExecution execution = node.getExecution();
LOGGER.fine("FlowExecution.isComplete: " + execution.isComplete());
if (!CpsFlowExecution.class.isInstance(execution)) {
throw new IllegalStateException("Cannot get a pipeline result from a FlowExecution that is not an instance of CpsFlowExecution: " + execution.getClass().getName());
}
CpsFlowExecution cpsFlowExecution = (CpsFlowExecution) execution;
LOGGER.fine("CpsFlowExecution.result: " + cpsFlowExecution.getResult());
BuildDetailsResolver.updateResultAndEndTime(details, cpsFlowExecution, new Date());
LOGGER.fine("Persisting BuildDetails");
repository.saveBuildDetails(details);
LOGGER.fine("Audit2DbGraphListener.onNewHead(): complete");
}
use of org.jenkinsci.plugins.workflow.graph.FlowEndNode in project workflow-cps-plugin by jenkinsci.
the class CpsFlowExecution method onProgramEnd.
/**
* Record the end of the build. Note: we should always follow this with a call to {@link #saveOwner()} to persist the result.
* @param outcome success; or a normal failure (uncaught exception); or a fatal error in VM machinery
*/
synchronized void onProgramEnd(Outcome outcome) {
FlowNode head = new FlowEndNode(this, iotaStr(), (FlowStartNode) startNodes.pop(), result, getCurrentHeads().toArray(new FlowNode[0]));
if (outcome.isFailure()) {
head.addAction(new ErrorAction(outcome.getAbnormal()));
}
// shrink everything into a single new head
try {
if (heads != null) {
FlowHead first = getFirstHead();
first.setNewHead(head);
// After setting the final head
done = true;
heads.clear();
heads.put(first.getId(), first);
String tempIotaStr = Integer.toString(this.iota.get());
FlowHead lastHead = heads.get(first.getId());
if (lastHead == null || lastHead.get() == null || !(lastHead.get().getId().equals(tempIotaStr))) {
// Warning of problems with the final call to FlowHead.setNewHead
LOGGER.log(Level.WARNING, "Invalid final head for execution " + this.owner + " with head: " + lastHead);
}
}
} catch (Exception ex) {
done = true;
LOGGER.log(Level.WARNING, "Error trying to end execution " + this, ex);
}
try {
this.getStorage().flush();
} catch (IOException ioe) {
LOGGER.log(Level.WARNING, "Error flushing FlowNodeStorage to disk at end of run", ioe);
}
this.persistedClean = Boolean.TRUE;
}
use of org.jenkinsci.plugins.workflow.graph.FlowEndNode in project workflow-cps-plugin by jenkinsci.
the class FlowDurabilityTest method verifyCompletedCleanly.
/**
* Verifies all the universal post-build cleanup was done, regardless of pass/fail state.
*/
static void verifyCompletedCleanly(Jenkins j, WorkflowRun run) throws Exception {
// Assert that we have the appropriate flow graph entries
FlowExecution exec = run.getExecution();
List<FlowNode> heads = exec.getCurrentHeads();
Assert.assertEquals(1, heads.size());
verifyNoTasksRunning(j);
Assert.assertEquals(0, exec.getCurrentExecutions(false).get().size());
if (exec instanceof CpsFlowExecution) {
CpsFlowExecution cpsFlow = (CpsFlowExecution) exec;
assert cpsFlow.getStorage() != null;
Assert.assertFalse("Should always be able to retrieve script", StringUtils.isEmpty(cpsFlow.getScript()));
Assert.assertNull("We should have no Groovy shell left or that's a memory leak", cpsFlow.getShell());
Assert.assertNull("We should have no Groovy shell left or that's a memory leak", cpsFlow.getTrustedShell());
Assert.assertTrue(cpsFlow.done);
assert cpsFlow.isComplete();
assert cpsFlow.heads.size() == 1;
Map.Entry<Integer, FlowHead> finalHead = cpsFlow.heads.entrySet().iterator().next();
assert finalHead.getValue().get() instanceof FlowEndNode;
Assert.assertEquals(cpsFlow.storage.getNode(finalHead.getValue().get().getId()), finalHead.getValue().get());
}
verifyExecutionRemoved(run);
}
Aggregations