cinder chaos: volume/snapshot churn with extend mutations#37
Merged
Conversation
Capture the exact create/delete/no-op schedule the chaos engine draws for churnPlan() at seed 7 under testConfig() and a virtual clock, so the upcoming engine seam refactor is provably byte-for-byte for Neutron. The golden file is generated once with -update against the current engine and must never be regenerated afterwards: its unchanged bytes are the proof that generalizing the node seam and adding the mutate action do not perturb the Neutron decision schedule for a given seed. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Christian Berendt <berendt@b42labs.com>
Drop the Neutron interface from the engine's node closures: a Node's Create/Delete now capture their cloud client at graph-build time and operate on the shared resource.Resource, so the engine names no service. Per-operation retry and 404 delete-tolerance move into the builder closures, Config sheds ExternalNetworkID/OpTimeout and gains an optional Classify hook, and a create now publishes whatever it returned (with a failed flag) so a created-but-not-ready resource stays deletable and recorded while a doomed child is still skipped. The Neutron graph builder, its interface, retry wrappers, and error classifier move to a new internal/chaos/neutrongraph subpackage; the engine no longer imports neutron, executor, or plan. The seeded engine tests and the byte-identical golden decision schedule move with it and stay green, proving the refactor is invisible for Neutron. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Christian Berendt <berendt@b42labs.com>
Give the churn engine a third action class alongside create and delete: a mutation of a live instance (a Cinder volume extend) that is neither. Each step draws, with probability ResizeRatio, a mutation of a present, not-yet-mutated node carrying a Mutate closure; a node is mutated at most once per lifetime and re-armed when re-created. The draw is double-gated on the graph having mutable nodes and a positive ratio, so a Neutron graph never touches the RNG here and its decision schedule stays byte-for-byte identical — the golden schedule proves it. Mutations do not change the population, so the churn controller's economics are untouched; they consume one fan-out slot like any API call. Result and ChaosStats gain a Mutates counter (omitempty, with a conditional table row) so extend activity is visible in a run record even when OTEL export is off. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Christian Berendt <berendt@b42labs.com>
Give the Cinder scenario its own chaos block, mirroring the Neutron one (duration, interval, parallel, churn_ratio, target_fill) plus the block-storage-specific resize_ratio: the per-tick probability of extending a live, not-yet-resized volume to its planned target. The block is validated (nil-safe, ratios in [0,1], interval min <= max) and duplicated by design so a typo in either service's scenario still fails loudly under strict unmarshal. Every shipped Cinder profile now carries a chaos block (5m/30m/1h), so `cinder chaos --scenario scenarios/cinder/<profile>.yaml` runs without a --duration flag. The small fixture gains the matching block; because Scenario now holds a *Chaos, the profile-fixture test compares with reflect.DeepEqual instead of ==. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Christian Berendt <berendt@b42labs.com>
Map a Cinder plan onto the chaos engine: one volume node per volume (parentless, mutable to its planned resizeToGiB when it has a target) and one snapshot node per snapshot, parented on its source volume so the engine never snapshots an absent volume nor deletes a volume with live snapshots. Readiness is folded into each operation — a create or extend completes only at available, a delete only once the resource is gone — and each volume's snapshot/extend/delete operations are serialized behind a per-volume mutex. The builder drives every call through the cinder executor's retry policy, now exported as WithRetry. The engine gains one guard the cinder graph is the first to need: skip a mutation when the create failed. Neutron creates are all-or-nothing, but a Cinder create can succeed yet fail readiness (a volume stuck at error); that instance is published so it stays deletable and recorded, but must not be extended. Fake-backed tests cover the lifecycle, envelope, extend- once-per-lifetime, gigabytes bound, per-volume serialization, the not- ready and terminal-error paths, and schedule determinism. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Christian Berendt <berendt@b42labs.com>
Wire the cinder churn graph into a `cinder chaos` subcommand mirroring `neutron chaos`: the same flag surface and three-layer config merge (defaults < scenario chaos block < flags), plus --volume-type and the block-storage-specific --resize-ratio. It authenticates, resolves the volume type, pre-checks quota against the full plan envelope, builds the cindergraph, runs the churn, records the run with ChaosStats and the volume type, and — unless interrupted or --no-cleanup — tears the volumes and snapshots down by metadata (snapshots before volumes, each operation bounded by --timeout) and runs a leak check. An interrupted or --no-cleanup run prints the `cinder cleanup --run-id` reclaim hint. buildCinderPlanFromFlags now also returns the scenario so the command can read its chaos block; generate, apply, and monitor ignore it. Command tests mirror the neutron chaos tests: required scenario and duration, flag-over-block precedence, resize-ratio override, invalid-scenario short-circuit, and shipped profiles running without --duration. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Christian Berendt <berendt@b42labs.com>
Document the new `cinder chaos` churn/soak mode in §15: add it to the commands block and the phase/roadmap status, add a "Chaos duration" column to the profile table, and add a "Churn / soak mode (cinder chaos)" subsection covering the reuse of the Neutron churn engine, the volume/snapshot lifecycle, the readiness rule, extend-as-mutation with the new resize_ratio knob (default 0.3, --resize-ratio overrides, 0 disables), the ChaosStats/OTEL outputs, and metadata-based reclamation of an interrupted run. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Christian Berendt <berendt@b42labs.com>
berendt
marked this pull request as ready for review
July 3, 2026 08:29
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #32
Summary
Adds a
cinder chaoschurn/soak mode: it drives volume and snapshot lifecycle churn within a scenario envelope while treating volume extend as a mutation. Rather than fork the Neutron churn engine, this branch first generalizes the shared chaos engine so it names no service, then teaches it a third action class (mutate), and finally maps a Cinder plan onto it and wires up the command.Change set (in commit order)
3a02cc8Pin the Neutron churn decision schedule with a golden testCaptures the exact create/delete/no-op schedule the chaos engine draws for
churnPlan()at seed 7 under a virtual clock, generated once with-update. The unchanged bytes become the proof that the subsequent refactor and the new mutate action do not perturb the Neutron decision schedule for a given seed.3be9c47Generalize the chaos engine seam and relocate the Neutron builderDrops the Neutron interface from the engine's node closures — a
Node'sCreate/Deletenow capture their cloud client at graph-build time and operate on the sharedresource.Resource, so the engine names no service. Per-operation retry and 404 delete-tolerance move into the builder closures;ConfigshedsExternalNetworkID/OpTimeoutand gains an optionalClassifyhook; a create now publishes whatever it returned (with afailedflag) so a created-but-not-ready resource stays deletable and recorded while a doomed child is skipped. The Neutron graph builder, interface, retry wrappers, and error classifier move to a newinternal/chaos/neutrongraphsubpackage. The golden schedule moves with the tests and stays byte-identical, proving the refactor is invisible for Neutron.8be83a8Add a mutate action with a resize_ratio draw to the chaos engineAdds a third action class alongside create and delete: a mutation of a live instance (a Cinder volume extend). Each step draws, with probability
ResizeRatio, a mutation of a present, not-yet-mutated node carrying aMutateclosure; a node is mutated at most once per lifetime and re-armed when re-created. The draw is double-gated on the graph having mutable nodes and a positive ratio, so a Neutron graph never touches the RNG here and its golden decision schedule stays byte-for-byte identical.Result/ChaosStatsgain aMutatescounter (omitempty).2117368Add a chaos block with resize_ratio to the Cinder scenario schemaGives the Cinder scenario its own chaos block mirroring the Neutron one (duration, interval, parallel, churn_ratio, target_fill) plus the block-storage-specific
resize_ratio— the per-tick probability of extending a live, not-yet-resized volume to its planned target. The block is validated (nil-safe, ratios in[0,1], interval min <= max) and duplicated by design so a typo in either service's scenario fails loudly under strict unmarshal. Every shipped profile (5m/30m/1h) gains a chaos block, socinder chaos --scenario …runs without--duration.be61aaaAdd the Cinder churn graph builderMaps a Cinder plan onto the chaos engine: one parentless volume node per volume (mutable to its planned
resizeToGiBwhen it has a target) and one snapshot node parented on its source volume, so the engine never snapshots an absent volume nor deletes a volume with live snapshots. Readiness is folded into each operation (create/extend complete only atavailable, delete only once the resource is gone), and each volume's snapshot/extend/delete operations are serialized behind a per-volume mutex. Drives every call through the cinder executor's retry policy, now exported asWithRetry. Adds one engine guard the cinder graph is first to need: skip a mutation when the create failed (a Cinder create can succeed yet fail readiness — a volume stuck aterror— and must stay deletable/recorded but never extended).c6f1de2Add the cinder chaos commandWires the cinder churn graph into a
cinder chaossubcommand mirroringneutron chaos: same flag surface and three-layer config merge (defaults < scenario chaos block < flags), plus--volume-typeand the block-storage-specific--resize-ratio. It authenticates, resolves the volume type, pre-checks quota against the full plan envelope, builds the cindergraph, runs the churn, records the run withChaosStatsand the volume type, and — unless interrupted or--no-cleanup— tears volumes and snapshots down by metadata (snapshots before volumes, each bounded by--timeout) and runs a leak check. An interrupted or--no-cleanuprun prints thecinder cleanup --run-idreclaim hint.buildCinderPlanFromFlagsnow also returns the scenario so the command can read its chaos block.839fe45Document cinder chaos in the READMEDocuments the new mode in §15: adds it to the commands block and phase/roadmap status, adds a "Chaos duration" column to the profile table, and adds a "Churn / soak mode (cinder chaos)" subsection covering engine reuse, the volume/snapshot lifecycle, the readiness rule, extend-as-mutation with the new
resize_ratioknob (default 0.3,--resize-ratiooverrides, 0 disables), theChaosStats/OTEL outputs, and metadata-based reclamation of an interrupted run.Divergences from the issue
None visible from the commits: the change implements volume/snapshot churn with extend mutations inside the scenario chaos envelope as issue #32 requested. Note that the branch does more than the literal ask — it first generalizes the shared chaos engine and extracts the Neutron builder into
internal/chaos/neutrongraph— but this is enabling refactor, pinned byte-for-byte against a golden decision schedule so Neutron behavior is provably unchanged.Testing
The diff carries the tests with the code: a golden Neutron decision schedule, generalized engine tests, the new
internal/chaos/cindergraphengine/graph tests (lifecycle, envelope, extend-once-per-lifetime, gigabytes bound, per-volume serialization, not-ready and terminal-error paths, schedule determinism), scenario schema validation tests, and command tests mirroring the neutron chaos suite.Implemented by planwerk-agent 6b3f3ea with Claude:claude-opus-4-8