Skip to content

Commit c6b4fa9

Browse files
committed
Adjust implementation to attempt to resume any step that has a FlowNode
1 parent 7b934f1 commit c6b4fa9

File tree

21 files changed

+20
-15
lines changed

21 files changed

+20
-15
lines changed

src/main/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionList.java

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -362,15 +362,14 @@ private static final class ParallelResumer {
362362
nodes.put(n, se);
363363
} else {
364364
LOGGER.warning(() -> "Could not find FlowNode for " + se + " so it will not be resumed");
365-
// TODO: Should we call se.getContext().onFailure()?
366365
}
367366
} catch (IOException | InterruptedException x) {
368367
LOGGER.log(Level.WARNING, "Could not look up FlowNode for " + se + " so it will not be resumed", x);
369-
// TODO: Should we call se.getContext().onFailure()?
370368
}
371369
}
372-
try {
373-
for (FlowNode n : nodes.keySet()) {
370+
for (Map.Entry<FlowNode, StepExecution> entry : nodes.entrySet()) {
371+
FlowNode n = entry.getKey();
372+
try {
374373
LinearBlockHoppingScanner scanner = new LinearBlockHoppingScanner();
375374
scanner.setup(n);
376375
for (FlowNode parent : scanner) {
@@ -379,14 +378,9 @@ private static final class ParallelResumer {
379378
break;
380379
}
381380
}
381+
} catch (Exception x) {
382+
LOGGER.log(Level.WARNING, x, () -> "Unable to compute enclosing blocks for " + n + ", so " + entry.getValue() + " might not resume successfully");
382383
}
383-
} catch (Exception e) {
384-
// TODO: Should we instead just try to resume steps with no respect to topological order?
385-
// There is something wrong with the FlowGraph. Kill all of the steps so the build fails instead of hanging forever.
386-
for (StepExecution se : executions) {
387-
se.getContext().onFailure(e);
388-
}
389-
onCompletion.run();
390384
}
391385
}
392386

src/main/java/org/jenkinsci/plugins/workflow/graph/StandardGraphLookupView.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ BlockStartNode bruteForceScanForEnclosingBlock(@NonNull final FlowNode node) {
124124
Set<String> visited = new HashSet<>();
125125
while (!(current instanceof FlowStartNode)) { // Hunt back for enclosing blocks, a potentially expensive operation
126126
if (!visited.add(current.getId())) {
127-
throw new IllegalStateException("Loop in flow graph for " + node.getExecution() + " involving " + current);
127+
throw new IllegalStateException("Cycle in flow graph for " + node.getExecution() + " involving " + current);
128128
}
129129
if (current instanceof BlockEndNode) {
130130
// Hop over the block to the start

src/main/java/org/jenkinsci/plugins/workflow/graphanalysis/LinearBlockHoppingScanner.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ protected FlowNode jumpBlockScan(@CheckForNull FlowNode node, @NonNull Collectio
9191
// Find the first candidate node preceding a block... and filtering by blacklist
9292
while (candidate instanceof BlockEndNode) {
9393
if (!visited.add(candidate.getId())) {
94-
throw new IllegalStateException("Loop in flow graph for " + candidate.getExecution() + " involving " + candidate);
94+
throw new IllegalStateException("Cycle in flow graph for " + candidate.getExecution() + " involving " + candidate);
9595
}
9696
candidate = ((BlockEndNode) candidate).getStartNode();
9797
if (blacklistNodes.contains(candidate)) {

src/test/java/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,12 @@
4141
import java.time.Duration;
4242
import java.time.Instant;
4343
import java.util.Collections;
44+
import java.util.Objects;
4445
import java.util.Set;
4546
import java.util.function.Supplier;
4647
import java.util.logging.Level;
48+
import java.util.logging.LogRecord;
49+
import java.util.stream.Collectors;
4750
import org.hamcrest.Matcher;
4851
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
4952
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
@@ -162,7 +165,7 @@ public class FlowExecutionListTest {
162165
}
163166

164167
@LocalData
165-
@Test public void resumeStepExecutionsWithCorruptFlowGraphWithLoop() throws Throwable {
168+
@Test public void resumeStepExecutionsWithCorruptFlowGraphWithCycle() throws Throwable {
166169
// LocalData created using the following snippet while the build was waiting in the _second_ sleep, except
167170
// for build.xml, which was captured during the sleep step. The StepEndNode for the stage was then adjusted to
168171
// have its startId point to the timeout step's StepStartNode, creating a loop.
@@ -175,10 +178,18 @@ public class FlowExecutionListTest {
175178
r.waitForCompletion(b);
176179
});
177180
*/
181+
logging.capture(50);
178182
sessions.then(r -> {
179183
var p = r.jenkins.getItemByFullName("test0", WorkflowJob.class);
180184
var b = p.getBuildByNumber(1);
181-
r.assertBuildStatus(Result.FAILURE, r.waitForCompletion(b));
185+
r.waitForCompletion(b);
186+
assertThat(logging.getMessages(), hasItem(containsString("Unable to compute enclosing blocks")));
187+
var loggedExceptions = logging.getRecords().stream()
188+
.map(LogRecord::getThrown)
189+
.filter(Objects::nonNull)
190+
.map(Throwable::toString)
191+
.collect(Collectors.toList());
192+
assertThat(loggedExceptions, hasItem(containsString("Cycle in flow graph")));
182193
});
183194
}
184195

src/test/resources/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest/resumeStepExecutionsWithCorruptFlowGraphWithLoop/jobs/test0/builds/1/build.xml renamed to src/test/resources/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest/resumeStepExecutionsWithCorruptFlowGraphWithCycle/jobs/test0/builds/1/build.xml

File renamed without changes.

src/test/resources/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest/resumeStepExecutionsWithCorruptFlowGraphWithLoop/jobs/test0/builds/1/log renamed to src/test/resources/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest/resumeStepExecutionsWithCorruptFlowGraphWithCycle/jobs/test0/builds/1/log

File renamed without changes.

src/test/resources/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest/resumeStepExecutionsWithCorruptFlowGraphWithLoop/jobs/test0/builds/1/log-index renamed to src/test/resources/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest/resumeStepExecutionsWithCorruptFlowGraphWithCycle/jobs/test0/builds/1/log-index

File renamed without changes.

src/test/resources/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest/resumeStepExecutionsWithCorruptFlowGraphWithLoop/jobs/test0/builds/1/program.dat renamed to src/test/resources/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest/resumeStepExecutionsWithCorruptFlowGraphWithCycle/jobs/test0/builds/1/program.dat

File renamed without changes.

src/test/resources/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest/resumeStepExecutionsWithCorruptFlowGraphWithLoop/jobs/test0/builds/1/workflow/10.xml renamed to src/test/resources/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest/resumeStepExecutionsWithCorruptFlowGraphWithCycle/jobs/test0/builds/1/workflow/10.xml

File renamed without changes.

src/test/resources/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest/resumeStepExecutionsWithCorruptFlowGraphWithLoop/jobs/test0/builds/1/workflow/2.xml renamed to src/test/resources/org/jenkinsci/plugins/workflow/flow/FlowExecutionListTest/resumeStepExecutionsWithCorruptFlowGraphWithCycle/jobs/test0/builds/1/workflow/2.xml

File renamed without changes.

0 commit comments

Comments
 (0)