Repository navigation
Conversation
mpereira
force-pushed
the
feat/chaos-experiment-harness/mpereira
branch
2 times, most recently
from
October 29, 2026 16:09
c1b50e2 to
14677b8
Compare
Add e2e/chaos, a harness that runs declarative resilience experiments against an OpenShell gateway and decides a verdict from recorded evidence: PASS, XFAIL, FAIL or ERROR. An experiment is one YAML file under e2e/chaos/experiments/ (apiVersion chaos/v1alpha1): the workloads to run as sandboxes, the operations to apply (kill, restart, pause, stop and start, policy changes), a flow that orders them in time, the metrics it is about, and per-instance and experiment-level checks, including bounds on scraped metrics. An expectations block ties checks to a GitHub issue, so a known bug ends the run in XFAIL and its fix shows up as XPASS. An adapter is a second YAML file under e2e/chaos/adapters/ that binds the harness to one system on one backend: the hooks that create, read, collect and delete a sandbox, the processes and containers or pods the built-in faults act on, adapter operations with their revert hooks, and what to observe. openshell-docker runs under e2e/with-docker-gateway.sh and is the one every result below came from. openshell-kubernetes declares the operations it cannot do and validates against every experiment, but has not been run against a cluster. A run writes e2e/chaos/runs/<experiment>/<run id>/ with every input, a timeline of state changes and operations, gateway and sandbox logs, scraped gateway metrics, the files each sandbox wrote and the verdict. From a run, chaos report draws an HTML report, chaos share writes a Markdown page with Mermaid charts for an issue or a post, chaos export writes the metrics as OpenMetrics, chaos dashboard builds the experiment's Grafana dashboard, and chaos evaluate decides the verdict again offline. grafana/ holds the Prometheus and Grafana compose stack. Nine experiments are included, all run on a local Docker gateway: - hello-world, smoke: harness sanity checks. PASS. - gateway-graceful-restart: SIGTERM the gateway and start it; every sandbox restarts and each counter resumes from its saved tick. PASS. - supervisor-pause: SIGSTOP every supervisor for 10 s; workloads with no network use run through untouched. PASS. - gateway-kill-keeps-state, policy-tighten-survives-gateway-restart: SIGKILL the gateway; the running sandboxes stop and never record an exit code. XFAIL, NVIDIA#4910. - sandbox-container-restart: docker restart one sandbox container; the sandbox never returns to Ready. XFAIL, NVIDIA#4828. - sandbox-stop-start: stop and start sandboxes for 25 minutes, timed against reconcile sweeps; one start ends StartFailed. XFAIL, NVIDIA#4981. - supervisor-kill: SIGKILL two supervisors; the sandboxes go to Error and stop, as documented. FAIL until the intended end state is settled. Outside e2e/chaos: e2e/with-docker-gateway.sh gains a metrics port and an opt-in OTLP endpoint (OPENSHELL_E2E_GATEWAY_OTLP=1), tasks/test.toml gains the e2e:chaos, e2e:chaos:preflight, e2e:chaos:check, e2e:chaos:share and e2e:chaos:grafana tasks, and CONTRIBUTING.md links the directory. Nothing runs in CI; e2e:chaos:check runs hello-world and smoke against a Docker gateway and fails unless both pass. Docs: e2e/chaos/README.md (overview, walkthrough, the run lifecycle, results, how to write an experiment), REFERENCE.md (the experiment and adapter schemas, CLI, run directory, timeline events, verdicts) and ADAPTER.md (how to write an adapter), with report, share and Grafana screenshots under images/. Signed-off-by: Murilo Pereira <murilo@murilopereira.com>
mpereira
force-pushed
the
feat/chaos-experiment-harness/mpereira
branch
from
October 29, 2026 16:19
14677b8 to
5959e19
Compare
`chaos share` wrote xychart values with Python's `g` format, which prints small values such as per-tick lock waits in exponent form (`9e-05`). Mermaid's xychart parser rejects that, so `--png` failed on those runs and GitHub would not draw the chart. Format every xychart value and axis bound with four significant digits in plain decimal, and cover it with a share-page test whose series holds values like 0.00009. Signed-off-by: Murilo Pereira <murilo@murilopereira.com>
6 tasks done
gateway-sigterm-during-recovery restarts the gateway gracefully, then sends a second SIGTERM while the restarted gateway's startup recovery sweep is starting the stopped sandboxes. On main the sweep runs before the SIGTERM handler is installed, so the gateway exits without a graceful shutdown and the next gateway retains the interrupted start's pending driver operation, which blocks starting and deleting that sandbox (NVIDIA#4409). Four runs on main all reproduced it. Signed-off-by: Murilo Pereira <murilo@murilopereira.com>
Render the Mermaid gantt of the latest gateway-sigterm-during-recovery run with chaos share --png, as the other experiments have, for the results table that links each experiment to its chart. Signed-off-by: Murilo Pereira <murilo@murilopereira.com>
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.
Over the last couple of days I've been playing around with ideas for running OpenShell fault-injection tests. I came up with a prototype harness that executes declarative experiments. An experiment has workloads and operations (a fault such as
killorfreeze, or a policy change), sequenced by a flow, and checks on the state of the system that decide the verdict: did the experiment pass or fail. e2e/chaos/README.md documents all of this.Summary
e2e/chaosis a fault-injection harness for OpenShell. Its experiments reproduce NVIDIA#4910, NVIDIA#4828, NVIDIA#4981 and NVIDIA#4409 in one command each. The branch adds the harness undere2e/chaosin four commits, and it touches nothing undercrates/ordocs/.The OpenShell gateway creates sandboxes and reconciles their state, and each sandbox runs with a supervisor that enforces its policy. The failures that matter happen between those three parts: a gateway that dies while sandboxes run, a supervisor whose processes are frozen, a policy that changes under a running sandbox. An experiment reaches them because it runs real sandboxes against a real gateway, disrupts them on purpose and records everything that happened. The e2e suites in
tests/check what a sandbox does in normal operation. The harness adds operations that change or disrupt the parts of the system, a flow that times them, evidence recorded whether the run passes or not, and a verdict that can expect a known failure.Because an experiment is one declarative file with a verdict, it serves as three things.
expectations, so a run tells a known failure (XFAIL) from a new one (FAIL), and a landed fix shows up as XPASS. Empty the expectation's checks and the experiment guards the fix from then on.The Python engine under
e2e/chaos/srcis a prototype that shows the model working. The model is the part I am putting forward: the experiment manifest, the adapter manifest and the run directory are the contract, and an engine built on the Rust testkit intests/testkit/, or any other engine, can run the same experiments and write the same runs. The README documents the model, and its section What a Rust engine would reuse says which parts such an engine would keep.The diagram below, the same one that opens the README, shows what one run takes in and what it leaves behind.
%%{init: {"flowchart": {"wrappingWidth": 600}, "themeVariables": {"fontSize": "20px"}}}%% flowchart LR subgraph experiment ["experiment manifest"] e["<pre>id:#nbsp;supervisor-kill#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>default-adapter:#nbsp;openshell-docker#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>workloads:#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;-#nbsp;name:#nbsp;counter#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;kind:#nbsp;shell#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;instances:#nbsp;3#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;run:#nbsp;|#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;for#nbsp;i#nbsp;in#nbsp;$(seq#nbsp;1#nbsp;#quot;$TICKS#quot;);#nbsp;do#nbsp;...#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;checks:#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;-#nbsp;name:#nbsp;exited-ok#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;kind:#nbsp;exit-code#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;equals:#nbsp;0#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>operations:#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;-#nbsp;name:#nbsp;kill-supervisors#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;uses:#nbsp;kill#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;target:#nbsp;{workload:#nbsp;counter,#nbsp;role:#nbsp;supervisor}#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;select:#nbsp;{count:#nbsp;2}#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>flow:#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;parallel:#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;-#nbsp;counter#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;-#nbsp;serial:#nbsp;[{wait:#nbsp;...},#nbsp;{delay:#nbsp;10s},#nbsp;kill-supervisors]</pre>"] end subgraph adapter ["adapter manifest"] a["<pre>id:#nbsp;openshell-docker#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>backend:#nbsp;docker#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>instance:#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;create:#nbsp;|#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;openshell#nbsp;sandbox#nbsp;create#nbsp;...#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;collect:#nbsp;|#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;openshell#nbsp;sandbox#nbsp;download#nbsp;...#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>targets:#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;gateway:#nbsp;{type:#nbsp;process,#nbsp;pid-file:#nbsp;...}#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;roles:#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;sandbox:#nbsp;{type:#nbsp;container,#nbsp;labels:#nbsp;...}#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;supervisor:#nbsp;{type:#nbsp;container,#nbsp;labels:#nbsp;...}#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>operations:#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;openshell-policy-set:#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;run:#nbsp;|#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;openshell#nbsp;policy#nbsp;set#nbsp;#quot;$(sandbox_name)#quot;#nbsp;--policy#nbsp;...#nbsp;#nbsp;#nbsp;#nbsp;<br/>metrics:#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;-#nbsp;{name:#nbsp;gateway,#nbsp;url:#nbsp;http://127.0.0.1:.../metrics}#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>watch:#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;-#nbsp;{name:#nbsp;docker-events,#nbsp;run:#nbsp;docker#nbsp;events#nbsp;...}#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;-#nbsp;{name:#nbsp;otel,#nbsp;run:#nbsp;docker#nbsp;run#nbsp;...#nbsp;opentelemetry-collector}</pre>"] end experiment -- "the argument" --> chaos["<pre>chaos#nbsp;run#nbsp;experiments/supervisor-kill.yaml</pre>"] adapter -- "named by default-adapter,<br/>or by --adapter ID" --> chaos subgraph ops ["what runs against the system"] direction TB h["adapter hooks<br/><pre>openshell#nbsp;sandbox#nbsp;create#nbsp;#nbsp;<br/>openshell#nbsp;sandbox#nbsp;download<br/>openshell#nbsp;policy#nbsp;set#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;</pre>"] f["built-in faults<br/><pre>kill#nbsp;-TERM,#nbsp;kill#nbsp;-STOP#nbsp;#nbsp;#nbsp;<br/>docker#nbsp;kill,#nbsp;docker#nbsp;pause</pre>"] o["observers<br/><pre>GET#nbsp;/metrics#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>docker#nbsp;events#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>OTLP#nbsp;spans#nbsp;from#nbsp;the#nbsp;gateway</pre>"] end chaos --> ops ops <--> sut subgraph sut ["system under test"] direction TB gw["OpenShell gateway process<br/>on the Docker backend"] subgraph inst ["per instance"] direction LR sb["sandbox container"] --- sv["supervisor container"] end gw --- inst end chaos -- "creates" --> run subgraph run ["experiment run, one directory"] r["<pre>runs/#lt;experiment#gt;/#lt;run#nbsp;id#gt;/#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>├──#nbsp;experiment.yaml#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>├──#nbsp;adapter.yaml#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>├──#nbsp;manifest.json#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>├──#nbsp;timeline.jsonl#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>├──#nbsp;verdict.json#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>├──#nbsp;report.html#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>├──#nbsp;logs/#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>│#nbsp;#nbsp;#nbsp;├──#nbsp;harness.log#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>│#nbsp;#nbsp;#nbsp;└──#nbsp;gateway.log#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>├──#nbsp;system/#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>│#nbsp;#nbsp;#nbsp;├──#nbsp;metrics/gateway.jsonl#nbsp;#nbsp;#nbsp;<br/>│#nbsp;#nbsp;#nbsp;└──#nbsp;watch/#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>│#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;├──#nbsp;docker-events.jsonl#nbsp;<br/>│#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;└──#nbsp;otel.jsonl#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>└──#nbsp;workloads/counter/instances/<br/>#nbsp;#nbsp;#nbsp;#nbsp;└──#nbsp;counter-0/#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;├──#nbsp;logs/sandbox.log#nbsp;#nbsp;#nbsp;#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;├──#nbsp;logs/supervisor.log#nbsp;<br/>#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;└──#nbsp;output/journal#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;</pre>"] endIn the diagram:
restart,kill,outage,restart-in-placeorfreezedisrupts one part of the system through the backend, Docker or Kubernetes. An adapter operation such asopenshell-policy-setruns a hook that calls the system's own commands.openshell-dockerandopenshell-kubernetes, and an experiment runs unchanged on either.chaos runtakes one experiment and one adapter and writes one experiment run: a self-contained directory with every input, the timeline, logs, metrics, the files each instance wrote and the verdict.chaos report,chaos share,chaos dashboardandchaos evaluateread only that directory, so a verdict can be decided again offline.Related Issue
This PR targets my fork, so the
state:acceptedgate does not apply. The experiments reproduce NVIDIA#4910, NVIDIA#4828, NVIDIA#4981 and NVIDIA#4409, and they close none of them.Results
Below is the latest run of each shipped experiment, with the chart
chaos sharedraws for it. All ran on theopenshell-dockeradapter (the harness's binding to OpenShell on the Docker compute driver) with Docker Desktop 29.8.2 on macOS on Apple silicon. The runs came from earlier states of this branch, before it was squashed into one commit, and each run'smanifest.jsonrecords the commit it ran at. Each run directory is undere2e/chaos/runs/<experiment>/<run>/, and the gateway under test was built frommainat 9f1cdb4. The verdict is PASS when every check holds, XFAIL (exit 0) when the only failures are ones an expectation names, and FAIL (exit 1) otherwise. ERROR overrides all three when the harness could not finish the run cleanly, for example when a sandbox could not be deleted.hello-worldA sandbox says hello.
smokeCounters run to completion with no faults.
supervisor-pauseA workload runs to completion while its supervisor is frozen.
gateway-graceful-restartCounter instances resume after a graceful gateway restart.
supervisor-killA sandbox survives its supervisor being killed.
gateway-kill-keeps-stateSandboxes survive a hard gateway kill.
policy-tighten-survives-gateway-restartA tightened policy stays in effect across a gateway restart.
sandbox-container-restartA sandbox returns to Ready after its container restarts.
sandbox-stop-startA stopped sandbox starts again every time.
gateway-sigterm-during-recoveryA SIGTERM during the gateway's startup recovery stops it gracefully.
In each chart the top section is the flow, with the faults in red, and every section under it is one sandbox instance from Created to Succeeded or Failed. In supervisor-kill, a killed supervisor stops the workload,
--restart-policy on-failuredoes not start it again, and no exit code is recorded. In gateway-kill-keeps-state the three counters are Unknown to the harness while the gateway is down (their workloads keep ticking), come back as Running once the gateway is up although the restarted gateway has already removed their supervisors, and then end Pending and Failed. A flaky expectation is one the failure need not hit on every run. The verdict records which instances it hit, and in the sandbox-stop-start run two of the eight sandboxes did, so its chart collapses the twenty restarts into one row and keeps only those two sandboxes. For what the two experiments for #4910 observed, see the comment on NVIDIA#4910: .gateway-sigterm-during-recovery came later than the other nine. Its four runs used the third commit, against a gateway built from
mainat 3a5dfcd. The chart is the latest run and shows four of the eight sandboxes, failed ones first; the README section charts the first run. A second SIGTERM lands while the restarted gateway's startup recovery is starting the sandboxes again, before the gateway installs its SIGTERM handler. The gateway exits without logging a shutdown, the next gateway keeps the interrupted start's pending driver operation, and that sandbox can then be neither started nor deleted. Every run hit it on one sandbox. The failed delete is a run error, so the verdict is ERROR and not XFAIL, and the sandbox's container is left running. In every run the gateway after the kill also failed to start one or two of the other sandboxes withsandbox runtime is already bound to another supervisor process, which no issue covers yet.The HTML report for the gateway-kill-keeps-state run, and the same run loaded into Grafana, look like this.
How to run one experiment
The three commands below clone the branch and run the experiment that reproduces #4910. The mise task builds and starts a local Docker gateway, runs the experiment against it, and writes a run directory with a timeline, a verdict and an HTML report. The run takes about 2 minutes after the first build, and it needs local Docker,
uvandjq.git clone -b feat/chaos-experiment-harness/mpereira https://github.com/mpereira/OpenShell cd OpenShell mise run e2e:chaos e2e/chaos/experiments/gateway-kill-keeps-state.yamlThe run exits 0 with the verdict XFAIL and writes its directory under
e2e/chaos/runs/gateway-kill-keeps-state/.Changes
The commits add these pieces.
e2e/chaos/holds thechaosCLI (validate,preflight,run,evaluate,report,share,export,dashboard,list,show), two adapters (openshell-dockerandopenshell-kubernetes), ten experiments with their latest runs, a Grafana loader, and the README, REFERENCE and ADAPTER docs.tasks/test.tomlgainse2e:chaos,e2e:chaos:preflight,e2e:chaos:check,e2e:chaos:shareande2e:chaos:grafana.e2e/with-docker-gateway.shnow always exposes the gateway's Prometheus metrics port. WithOPENSHELL_E2E_GATEWAY_OTLP=1it points the gateway's OTLP trace export at a port the harness's collector listens on, so an experiment can wait for the gateway's reconcile sweep (the periodic pass that re-checks every sandbox) instead of sleeping.CONTRIBUTING.mdgains one line for the new directory.Release Notes
This branch changes no user-facing behaviour.
Testing
Here is what I checked and what I did not.
e2e/chaos/scripts/test-and-lint.shpasses on the branch head: 253 unit tests, ruff check and format, and shellcheck on the scripts and the embedded shell.uv run --project e2e/chaos chaos validate --all-adapterspasses. Every experiment validates onopenshell-docker. Seven validate onopenshell-kubernetes, and three are marked not applicable by design, because in-place restart, freeze and the reconcile-sweep signal have no Kubernetes equivalent.e2e:chaos:check, which requires hello-world and smoke to PASS, is the candidate.e2e/chaos/tests.Checklist
e2e/chaos/README.mddocuments the harness.Questions
Three questions for OpenShell maintainers who read this from the issue comments:
--restart-policy on-failurecover it?sandbox startduring the reconcile sweep should now always reach Ready, or is some residual flakiness expected?