This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new d3f206202ebb chore: camel-chaos - Times out counts as tried once it
ran, and let there be chaos remembers the budget and the picks (#29623)
d3f206202ebb is described below
commit d3f206202ebb23210f86c250197931ceef6a8778
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Nov 11 14:17:38 2026 +0100
chore: camel-chaos - Times out counts as tried once it ran, and let there
be chaos remembers the budget and the picks (#29623)
* chore: camel-chaos - Times out counts as tried once it ran, and let there
be chaos remembers the budget and the picks
A rule that fails after a wait (after=) is a timeout: kind slow, as the
suggestions offer it, not errors.
The Chaos screen (L) opens with the budget and the rules picked the last
time, while the TUI runs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
* chore: camel-jbang - the picks of the chaos explore dialog are an
unmodifiable set
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---------
Signed-off-by: Claus Ibsen <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
---
.../org/apache/camel/catalog/docs/chaos.adoc | 6 ++--
components/camel-chaos/src/main/docs/chaos.adoc | 6 ++--
.../apache/camel/component/chaos/ChaosAdvisor.java | 3 +-
.../camel/component/chaos/ChaosTriedTest.java | 3 ++
.../dsl/jbang/core/commands/tui/ChaosControl.java | 13 +++++++--
.../core/commands/tui/ChaosExploreDialog.java | 32 ++++++++++++++++++++++
.../jbang/core/commands/tui/ChaosExploreTest.java | 30 ++++++++++++++++++++
7 files changed, 84 insertions(+), 9 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/chaos.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/chaos.adoc
index 3cf17107f73d..2a245ccca8a8 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/chaos.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/chaos.adoc
@@ -324,9 +324,9 @@ $ camel get chaos --report
shipping dead letter channel not tried yet
----
-* The kinds are `errors` (a send or a step fails), `slow` (a delay), `lost` (a
send is dropped), `corrupt`, `duplicate`,
-`consumer` (the polls, or the entry of the route), `lifecycle` (stall and
lifecycle) and `dead letter channel`
-(`at=dlq`). A rule has one kind, from its fault and where it acts.
+* The kinds are `errors` (a send or a step fails), `slow` (a delay, or an
error that waits first: a timeout), `lost`
+(a send is dropped), `corrupt`, `duplicate`, `consumer` (the polls, or the
entry of the route), `lifecycle` (stall and
+lifecycle) and `dead letter channel` (`at=dlq`). A rule has one kind, from its
fault and where it acts.
* A kind is tried in a route when a rule of that kind fired in it: a rule
counts its faults per route, so a rule that
reaches several routes is tried in each. A rule that expects no message lost
between two routes
(`expect=orders->shipping:lost<=0`) also tries `lost` for the first route, as
the hand-off suggestion fails the next
diff --git a/components/camel-chaos/src/main/docs/chaos.adoc
b/components/camel-chaos/src/main/docs/chaos.adoc
index 3cf17107f73d..2a245ccca8a8 100644
--- a/components/camel-chaos/src/main/docs/chaos.adoc
+++ b/components/camel-chaos/src/main/docs/chaos.adoc
@@ -324,9 +324,9 @@ $ camel get chaos --report
shipping dead letter channel not tried yet
----
-* The kinds are `errors` (a send or a step fails), `slow` (a delay), `lost` (a
send is dropped), `corrupt`, `duplicate`,
-`consumer` (the polls, or the entry of the route), `lifecycle` (stall and
lifecycle) and `dead letter channel`
-(`at=dlq`). A rule has one kind, from its fault and where it acts.
+* The kinds are `errors` (a send or a step fails), `slow` (a delay, or an
error that waits first: a timeout), `lost`
+(a send is dropped), `corrupt`, `duplicate`, `consumer` (the polls, or the
entry of the route), `lifecycle` (stall and
+lifecycle) and `dead letter channel` (`at=dlq`). A rule has one kind, from its
fault and where it acts.
* A kind is tried in a route when a rule of that kind fired in it: a rule
counts its faults per route, so a rule that
reaches several routes is tried in each. A rule that expects no message lost
between two routes
(`expect=orders->shipping:lost<=0`) also tries `lost` for the first route, as
the hand-off suggestion fails the next
diff --git
a/components/camel-chaos/src/main/java/org/apache/camel/component/chaos/ChaosAdvisor.java
b/components/camel-chaos/src/main/java/org/apache/camel/component/chaos/ChaosAdvisor.java
index 4f65002691ee..6c5db1c6c0a5 100644
---
a/components/camel-chaos/src/main/java/org/apache/camel/component/chaos/ChaosAdvisor.java
+++
b/components/camel-chaos/src/main/java/org/apache/camel/component/chaos/ChaosAdvisor.java
@@ -179,7 +179,8 @@ final class ChaosAdvisor {
case DROP -> consumer ? Kind.CONSUMER : Kind.LOST;
case CORRUPT -> Kind.CORRUPT;
case DUPLICATE -> Kind.DUPLICATE;
- default -> consumer ? Kind.CONSUMER : Kind.ERRORS;
+ // an error that waits first (after=) is a call that times out,
which the suggestions offer as slow
+ default -> consumer ? Kind.CONSUMER : rule.after > 0 ? Kind.SLOW :
Kind.ERRORS;
};
}
diff --git
a/components/camel-chaos/src/test/java/org/apache/camel/component/chaos/ChaosTriedTest.java
b/components/camel-chaos/src/test/java/org/apache/camel/component/chaos/ChaosTriedTest.java
index 92bd4826ff56..2db55f43e568 100644
---
a/components/camel-chaos/src/test/java/org/apache/camel/component/chaos/ChaosTriedTest.java
+++
b/components/camel-chaos/src/test/java/org/apache/camel/component/chaos/ChaosTriedTest.java
@@ -168,6 +168,9 @@ class ChaosTriedTest extends ChaosTestSupport {
void theKindOfARule() {
assertThat(kind("endpoint=x
error")).isEqualTo(ChaosAdvisor.Kind.ERRORS);
assertThat(kind("endpoint=x delay
duration=1s")).isEqualTo(ChaosAdvisor.Kind.SLOW);
+ // Times out: the suggestion of an HTTP call is slow, so trying it
marks it tried
+ assertThat(kind("endpoint=x error after=5s
exception=java.net.SocketTimeoutException"))
+ .isEqualTo(ChaosAdvisor.Kind.SLOW);
assertThat(kind("endpoint=x drop")).isEqualTo(ChaosAdvisor.Kind.LOST);
assertThat(kind("endpoint=x drop
at=poll")).isEqualTo(ChaosAdvisor.Kind.CONSUMER);
assertThat(kind("endpoint=x error
at=poll")).isEqualTo(ChaosAdvisor.Kind.CONSUMER);
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ChaosControl.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ChaosControl.java
index 93d290500f57..be7e1eb6def5 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ChaosControl.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ChaosControl.java
@@ -23,6 +23,7 @@ import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -96,6 +97,8 @@ final class ChaosControl {
// the exploration started here (L), and whether one was just started, for
the screen to show it
private final ChaosExploreRun explorer;
private boolean exploreStarted;
+ // the budget and the picks of the dialog per integration, as it was
closed last, while the TUI runs
+ private final Map<String, ChaosExploreDialog.Picks> lastPicks = new
ConcurrentHashMap<>();
// set by the background task that sends the action, read when the screen
is drawn
private volatile Step step = Step.NONE;
private Command command;
@@ -404,7 +407,9 @@ final class ChaosControl {
: reply.getString("error"));
return;
}
- exploreDialog = new ChaosExploreDialog(of, reply);
+ ChaosExploreDialog dialog = new ChaosExploreDialog(of, reply);
+ dialog.restore(lastPicks.get(of));
+ exploreDialog = dialog;
};
if (sender != null) {
task.run();
@@ -422,8 +427,12 @@ final class ChaosControl {
return true;
}
switch (dialog.handleKey(ke)) {
- case CLOSED -> close();
+ case CLOSED -> {
+ lastPicks.put(dialog.name(), dialog.picks());
+ close();
+ }
case START -> {
+ lastPicks.put(dialog.name(), dialog.picks());
explorer.start(pid, dialog.picked(), dialog.budget(),
dialog.budgetMillis());
exploreStarted = true;
close();
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ChaosExploreDialog.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ChaosExploreDialog.java
index 6d634cc4044b..8687a1218df1 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ChaosExploreDialog.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ChaosExploreDialog.java
@@ -95,6 +95,10 @@ final class ChaosExploreDialog {
}
}
+ /** The budget and the rules picked, to open the dialog with them again. */
+ record Picks(String budget, Set<String> rules) {
+ }
+
/** The budgets to pick from: an exploration by hand is short, the long
ones are for camel cmd chaos-explore. */
static final List<String> BUDGETS = List.of("2m", "5m", "10m", "15m",
"30m");
// the shortest time a rule is tried, so its faults fire and the routes
have messages
@@ -175,6 +179,34 @@ final class ChaosExploreDialog {
return items;
}
+ String name() {
+ return name;
+ }
+
+ /** The budget and the rules picked now. */
+ Picks picks() {
+ return new Picks(budget(),
picked().stream().map(Item::rule).collect(Collectors.toUnmodifiableSet()));
+ }
+
+ /**
+ * Opens with the budget and the picks of last time, to try them again
(after a change to the routes): the rules of
+ * last time that the integration still suggests, else the ones picked by
itself.
+ */
+ void restore(Picks last) {
+ if (last == null) {
+ return;
+ }
+ int index = BUDGETS.indexOf(last.budget());
+ if (index >= 0) {
+ budget = index;
+ }
+ if (items.stream().anyMatch(item ->
last.rules().contains(item.rule()))) {
+ for (int i = 0; i < items.size(); i++) {
+ picked.set(i, last.rules().contains(items.get(i).rule()));
+ }
+ }
+ }
+
/** The picked rules, in the order of the list. */
List<Item> picked() {
List<Item> answer = new ArrayList<>();
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ChaosExploreTest.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ChaosExploreTest.java
index 8fa1af8c414c..f52700e8436f 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ChaosExploreTest.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ChaosExploreTest.java
@@ -18,6 +18,7 @@ package org.apache.camel.dsl.jbang.core.commands.tui;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@@ -150,6 +151,35 @@ class ChaosExploreTest {
.isEqualTo(ChaosExploreDialog.Outcome.NONE);
}
+ @Test
+ void theDialogOpensWithTheBudgetAndThePicksOfLastTime() {
+ key('L');
+ // the first rule unpicked, refunds picked, and 5 minutes
+ key(' ');
+ press(KeyCode.DOWN);
+ press(KeyCode.DOWN);
+ key(' ');
+ press(KeyCode.LEFT);
+ press(KeyCode.ESCAPE);
+ assertThat(tab.isTextInputActive()).isFalse();
+
+ key('L');
+ assertThat(TuiTestHelper.renderToString(tab, 160, 40)).contains("5m",
"3 rules, 1m40s each",
+ "[ ] Returns an error (HTTP status)", "[x] The route after a
hand-off fails", "[x] The route fails",
+ "[x] An outage while the dead letter channel fails");
+ }
+
+ @Test
+ void thePicksOfLastTimeThatAreNoLongerSuggestedAreNot() throws Exception {
+ ChaosExploreDialog dialog = dialog();
+ dialog.restore(new ChaosExploreDialog.Picks("30m", Set.of("route=gone
error")));
+ assertThat(dialog.budget()).isEqualTo("30m");
+ // picked by itself, as none of last time is suggested
+ assertThat(dialog.picked()).extracting(ChaosExploreDialog.Item::title)
+ .containsExactly("Returns an error (HTTP status)", "The route
after a hand-off fails",
+ "An outage while the dead letter channel fails");
+ }
+
@Test
void eachRuleRunsForItsShare() {
assertThat(ChaosExploreRun.forShare(