allthingssecurity opened a new pull request, #28812:
URL: https://github.com/apache/camel/pull/28812

   # Description
   
   CAMEL-26456 (related to CAMEL-26330 and CAMEL-26418): **route lifecycle 
faults**, step 6 of "Order of work" in
   `design/chaos.adoc`. Step 8 (a DST world that runs chaos rules in virtual 
time) is a stacked follow-up PR on top of this
   one; steps 3 (the dev console and the CLI, #28788) and 7 (the MCP tool 
group) are in their own PRs. It is built on main and does not need #28788: the 
new rules go through `camel.chaos.rules`
   and `ChaosManager`, so the CLI and the MCP tools take them as they take any 
rule (the console JSON carries the new
   `lifecycle` object; see "After #28788" below).
   
   The rule syntax of the design already had these faults, and the parser 
rejected them as "not supported yet":
   
   ```
   route=poller stall duration=20s                        # suspend, resume 
after 20 s
   route=orders lifecycle action=suspend duration=1m      # the same
   route=orders lifecycle action=stop duration=1m         # stop, start after 1 
m
   route=orders lifecycle action=remove duration=10s      # remove, add the 
same route definition back after 10 s
   ```
   
   ## What a lifecycle rule does
   
   - **Once, when the rule is added**, as the design says (a rule of 
`camel.chaos.rules` when the context has started).
     `at`, `rate` and `until` are rejected (`rate= does not apply to the fault 
stall: it acts once on the route when the
     rule is added, and brings it back after its duration`), and the `duration` 
is at most `camel.chaos.maxDuration`. An
     expectation (`expect=orders:100 within=5m`) can be added.
   - **The route is checked by the same parser**: `route=` takes the id of one 
route (a pattern is rejected), which must
     exist when the rule is added (`there is no route nope (the fault stall 
acts on a route that exists when the rule is
     added)`). A rule of `camel.chaos.rules` is checked when the context starts 
(`onContextStarting`, with its routes
     created), and an unknown route stops the context from starting, as any 
rule that is not valid does. While a rule has
     a route removed, a rule for that route is still accepted (to change the 
rule).
   - **On the scheduled executor of chaos**: the action and the comeback are 
tasks of one
     `newSingleThreadScheduledExecutor` of the `ExecutorServiceManager`, 
created when the first lifecycle rule acts. So
     adding a rule returns at once (a suspend or stop can wait for the shutdown 
strategy), the tasks run one after another,
     and in a camel-test-dst world they run on the loop of the run. The route 
is suspended, stopped, resumed and started
     through `camelContext.getRouteController()`, and removed with 
`CamelContext.removeRoute`.
   - **`action=remove` adds the same route definition back** with 
`Model.addRouteDefinition`: the definition that was
     removed, as it was prepared, so the route comes back with the route 
configurations it had. That needs camel-core-model
     as a dependency of camel-chaos (as camel-kamelet and 
camel-observability-services have; nothing in camel-api adds a
     definition). A route created from a route template or a kamelet is 
rejected for `remove`: removing the route closes
     the local beans of its template (`LocalBeanRegistry`), which the 
definition still refers to, so it could not come
     back. `stop` and `stall` work for it.
   
   ## Chaos only brings back what it took down
   
   - A route that is not started when the rule acts is left alone (state 
`skipped`, "route a was stopped").
   - At the comeback, a route that someone else changed meanwhile (stopped a 
suspended route, removed it, or a reload in
     dev mode loaded a new route with the same id) is left as it is (`left`, 
with the reason).
   - Removing, changing or clearing the rule while the route is down brings it 
back at once (`endedEarly`). A changed rule
     acts after the old version brought the route back (not "skipped" because 
chaos itself had it down).
   - When the context stops, a pending comeback is cancelled and the executor 
shut down, so nothing of chaos keeps running
     (`cancelled`, "the context stopped, so route a was not resumed"). A 
removed route then stays removed when the context
     is started again; the docs say so.
   - A failed comeback (a route that fails to start) is reported `failed`. An 
action that fails halfway (a remove that
     could stop the route but not remove it) brings the route back as it left 
it, after the duration (`down`, then
     `back`, with the reason).
   - **Supervising route controller**: a stop or suspend through the route 
controller is a manual operation for
     `DefaultSupervisingRouteController` (it releases the route: 
`route.getRouteController()` is null while down), and the
     comeback starts the route through it, which supervises it again (tests pin 
both). A route that `remove` adds back is
     taken under supervision by the controller but not started by it, so chaos 
starts it through the controller (a test
     failed before that). Since that controller starts routes in the 
background, a rule of `camel.chaos.rules` can find its
     route not started yet and does nothing; the docs say so.
   - **The report** keeps what each lifecycle rule did, with the times after 
the rule was added (read through
     `TimeSource`):
   
   ```
       r2 ended: route=orders lifecycle action=suspend duration=60000ms 
expect=orders:8 within=50m
         matches 1, fired 1, skipped 0
         suspended route orders at +0ms, resumed at +1m0s (down 1m0s)
         expect orders:8 within 50m0s: met, 8 completed
   ```
   
     In JSON the action counts as `fired.lifecycle`, and the rule has a 
`lifecycle` object: `action`, `route`, `duration`,
     `state`, `downAt`, `backAt`, `backIn` while down, `endedEarly`, `reason`. 
A lifecycle rule has no `endsIn` (it ends
     when its route is back), and it is `ended` then. The report keeps removed 
rules as before, with their lifecycle state.
   - **Fail closed** as everything else: with profile `prod`, or without the 
profile `dev` or `camel.chaos.enabled=true`,
     nothing of chaos is installed, so these rules are ignored (a test pins it 
for a lifecycle rule in `camel.chaos.rules`).
   
   ## Docs
   
   - The chaos page: the faults table (`lifecycle`, `stall`), the targets 
table, a new section "Route lifecycle faults"
     (all of the above), and "Time and the seed". The catalog copy is 
regenerated.
   - `design/chaos.adoc`: the lifecycle row says the target is one route that 
exists, and what chaos does not bring back;
     "Order of work" marks the steps that are done (1, 2, 4, 5, and 6 with this 
PR). `last-updated` is left alone, as
     #28788 changes that line.
   - No upgrade guide entry: no existing option or rule changes behaviour 
(`stall` and `lifecycle` were rejected before).
   
   ## Tests
   
   New test classes (each run 20 times in its own JVM, all passed):
   
   - `ChaosLifecycleTest` (19): stall, stop and remove with their comeback (the 
same definition added back, with one chaos
     route policy, counting an exchange once); the route takes messages again; 
a route that is not started is left
     alone; a route stopped by someone while suspended, a route loaded again 
and a route replaced meanwhile are left as
     they are; removing the rule brings the route back at once, also while the 
action runs (a stop waiting for an exchange
     in flight); a rule changed while it acts acts after the old version 
brought the route back; change and clear end the
     old fault; a remove that fails after the stop starts the route after the 
duration; a comeback that fails is reported;
     a route from a route template is not removed but can be stopped; a context 
stop cancels the comeback (no executor, no
     `Chaos` thread left); rules act again after the context is started again; 
an expectation of a stalled route; the
     route checks (unknown route, max duration, `rate=`; a rejected rule takes 
no id).
   - `ChaosLifecycleStartupTest` (9): a rule of `camel.chaos.rules` acts when 
the context has started and waits while it
     is not; removed before the start, it does nothing; an unknown route or 
`until=` stops the context from starting;
     ignored with profile `prod`; the supervising route controller (stop: 
released while down, supervised again after;
     stall; remove: added back, started and supervised).
   - `ChaosRuleParserTest`: the forms of `stall` and `lifecycle`, every 
rejection with its message, and `action=` on
     another fault; the fuzz test accepts a lifecycle rule without hooks.
   
   The camel-chaos module passes (303 tests). Negative controls: 21 mutants of 
the new code, each compiled into the
   module and run against its test classes; 20 make a test fail (no comeback, a 
comeback at once or 1 ms early, acting on
   a stopped route, no "left" check, replacing a reloaded route, removing the 
rule keeps the route down, the context stop
   not told, a suspend that does nothing, `rate=`, a pattern or a too long 
duration accepted, the route not checked at add
   or at start, startup rules that never act, a changed rule not deferred, a 
re-added route not started under supervision,
   a stop that bypasses the route controller, a halfway failure left down, the 
report without `lifecycle`). One survives
   as equivalent: not shutting the executor down at stop, since the 
`ExecutorServiceManager` shuts its executors down when
   the context stops.
   
   ## Merge with the open chaos PRs
   
   `git merge-tree` against #28788 (`camel-chaos-control`) is clean. Against 
CAMEL-26432 (`camel-chaos-netty-http-status`)
   one line conflicts, in the table of rejected rules of `ChaosRuleParserTest`: 
this PR changes the expected message of
   `endpoint=x error action=stop` and that PR the line below it; whichever 
merges second keeps both lines.
   
   ## After #28788
   
   The `chaos` console of #28788 shows the report JSON, which then has the 
`lifecycle` object; its `RuleEntry` metadata
   and `camel get chaos` (the ENDS IN column is empty for a lifecycle rule) do 
not know it yet. I can add that in whichever
   of the two merges second, or as a follow-up JIRA.
   
   _Claude Code on behalf of allthingssecurity_
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to