Skip to content

chore(e2e): kind flake-fix grab-bag — image load, RPC waits, log routing#210

Closed
puneet2019 wants to merge 4 commits into
mainfrom
chore/e2e-kind-flake-fixes
Closed

chore(e2e): kind flake-fix grab-bag — image load, RPC waits, log routing#210
puneet2019 wants to merge 4 commits into
mainfrom
chore/e2e-kind-flake-fixes

Conversation

@puneet2019

Copy link
Copy Markdown
Contributor

Summary

Targets the most common e2e/kind local flakes diagnosed across recent runs. Each fix is small but targets a specific failure mode. Bundled into one PR since they share the same files (`lib.sh`, `framework.sh`).

Five fixes

1. Image-load: `kind load docker-image` -> `docker save | ctr import`

The default `kind load` silently fails with the desktop-linux buildx driver — the manifest gets exported but the image doesn't actually land in the cluster's containerd snapshotter under the expected SHA, leading to `ErrImageNeverPull` at pod start.

Replaced with new `kind_load_image` helper that uses `docker save | docker exec ctr import` directly.

2. Force kind delete when building fresh images

`setup-kind.sh` now deletes the existing cluster when `FW_SKIP_BUILD=false`. Stale clusters retain old image SHAs under the same tag in containerd → new build silently uses the old SHA. CI runners always start clean so this is a no-op there; locally it's the difference between "passes first try" and "fails mysteriously".

3. Per-pod RPC readiness

New `wait_for_all_validator_rpcs ` polls each validator pod's internal RPC after start / restart. The existing `wait_for_chain_ready` only checks the host's NodePort RPC, which one healthy pod satisfies — but a tx routed via kubectl exec to a different pod whose RPC isn't yet serving fails with empty stderr.

4. EVM RPC readiness

New `wait_for_evm_rpc_ready` polls `eth_blockNumber` until non-zero. Cosmos `/status` can be live before the EVM JSON-RPC is fully attached, leading to "server returned a null response" on first `cast send`.

5. log_error / log_warn -> stderr

`log_error` and `log_warn` wrote to stdout with no `>&2`. Any caller using `var=$(some_func)` captured the error message INTO `var` (hiding it from the log AND polluting downstream values). This was the root cause of several "Test exited early (code 1)" with no visible error reason — the error was getting eaten by command substitution.

Wire-up

  • `lib.sh`: new helpers `wait_for_all_validator_rpcs`, `wait_for_evm_rpc_ready`, `kind_load_image`. `log_error`/`log_warn` now to stderr.
  • `setup-kind.sh`: force-delete stale cluster when building.
  • `build-images.sh`, `framework.sh`, `upgrade-chain.sh`: use `kind_load_image`.
  • `framework.sh:fw_start_chain*`, `upgrade-chain.sh`: call new RPC waits after pod-ready.

5 files, +122/-14.

Test plan

  • `bash -n` syntax clean
  • `shellcheck -S info -x` clean (only pre-existing SC1091/SC1090/SC2064 warnings on lines I didn't touch)
  • Pre-upgrade phase now passes cleanly (was silent-failing locally before)

Known residual flake (out of scope)

Post-upgrade `evm_deploy` still has a window where `cast send` hits "null response" AFTER `eth_blockNumber` reports non-zero — likely `eth_estimateGas` / `eth_chainId` warming at a different rate. The new log routing makes this visible in logs (it was completely silent before). Separate follow-up.

Why draft

Marked draft because:

  • (5) routes existing log_error / log_warn output differently — anything currently parsing test logs for those colored markers from stdout will need to read stderr instead. Need a sanity-check that no tooling relies on the old behavior.
  • The residual cast-send flake means CI may still hit it occasionally.

🤖 Generated with Claude Code

Targets the most common e2e/kind local flakes diagnosed across the
last few runs. Each fix is small but targets a specific failure mode:

1. Image-load: replace `kind load docker-image` with docker save | ctr
   import via the new `kind_load_image` helper. The default kind load
   silently fails with the desktop-linux buildx driver — the manifest
   gets exported but the image doesn't actually land in the cluster's
   snapshotter under the expected SHA, leading to ErrImageNeverPull.

2. Force kind delete when building fresh images: `setup-kind.sh` now
   deletes the existing cluster when FW_SKIP_BUILD=false. Stale clusters
   retain old image SHAs under the same name in containerd, so a new
   build that loads under the same tag silently uses the old SHA.

3. Per-pod RPC readiness: `wait_for_all_validator_rpcs <num>` polls each
   validator pod's internal RPC after start / restart. The existing
   `wait_for_chain_ready` only checks the host's NodePort RPC, which
   one healthy pod satisfies — but a tx routed via kubectl exec to a
   different pod whose RPC isn't yet serving fails with empty stderr.

4. EVM RPC readiness: `wait_for_evm_rpc_ready` polls eth_blockNumber
   until non-zero. Cosmos /status can be live before the EVM JSON-RPC
   is fully attached after start / rolling restart, leading to "server
   returned a null response" on first cast send.

5. log_error / log_warn -> stderr: the helpers wrote to stdout with no
   `>&2`, so any caller using `var=$(some_func)` captured the error
   message INTO `var` (hiding it from the log AND polluting downstream
   values). Confirmed root cause of several "Test exited early" with
   no visible error reason.

Wire-up:
- `lib.sh`: new helpers `wait_for_all_validator_rpcs`, `wait_for_evm_rpc_ready`, `kind_load_image`; log_error/log_warn now to stderr
- `setup-kind.sh`: force-delete stale cluster when building
- `build-images.sh`, `framework.sh`, `upgrade-chain.sh`: use kind_load_image
- `framework.sh:fw_start_chain*`, `upgrade-chain.sh`: call new RPC waits

Verified locally: pre-upgrade phase passes cleanly (was previously
silent-failing); post-upgrade evm_deploy still has a residual flake
where cast send hits a null-response window AFTER EVM RPC reports
block > 0 but before some other path is fully warmed (eth_estimateGas
or similar). That's a separate fix; this PR closes the most common
flake classes.
@puneet2019 puneet2019 closed this Jul 8, 2026
puneet2019 added a commit that referenced this pull request Jul 8, 2026
… quorum

Root cause of the hardfork-upgrade flake ('Chain did not reach height 41
within 240s'): upgrade-chain.sh loaded the new-binary image with
'kind load docker-image ... 2>/dev/null || true' — a silent failure leaves
validators restarting into a missing/old image — and wait_for_chain_ready is
satisfied by a single healthy pod, so the harness declared 'chain resumed'
while 2/3 quorum was still absent. The post-upgrade height wait then burned
its whole budget measuring the wrong precondition.

- kind_load_image: docker save | ctr import into the kind node with explicit
  verification (crictl/ctr image listing), no silent-failure path; used by
  build-images.sh, framework.sh, and upgrade-chain.sh.
- wait_for_all_validator_rpcs: gate on EVERY validator serving its own RPC
  (real quorum signal) after rolling restarts, plus wait_for_evm_rpc_ready
  for the JSON-RPC attach lag.
- setup-kind.sh: delete stale clusters on fresh builds so containerd cannot
  serve an old image SHA under the new tag.

Carved from the closed grab-bag #210 (chore/e2e-kind-flake-fixes); the
log-to-stderr slice of that branch is #319 and is deliberately not included
here (no overlapping hunks; the two compose cleanly).

Validated end-to-end: full hardfork upgrade e2e (v1.3.0 -> current) 5/5 with
the new gates visible in the log, and the RPC suite 11/11 for framework
regression.

Signed-off-by: puneetmahajan <59960662+puneet2019@users.noreply.github.com>
puneet2019 added a commit that referenced this pull request Jul 8, 2026
… quorum

Root cause of the hardfork-upgrade flake ('Chain did not reach height 41
within 240s'): upgrade-chain.sh loaded the new-binary image with
'kind load docker-image ... 2>/dev/null || true' — a silent failure leaves
validators restarting into a missing/old image — and wait_for_chain_ready is
satisfied by a single healthy pod, so the harness declared 'chain resumed'
while 2/3 quorum was still absent. The post-upgrade height wait then burned
its whole budget measuring the wrong precondition.

- kind_load_image: docker save | ctr import into the kind node with explicit
  verification (crictl/ctr image listing), no silent-failure path; used by
  build-images.sh, framework.sh, and upgrade-chain.sh.
- wait_for_all_validator_rpcs: gate on EVERY validator serving its own RPC
  (real quorum signal) after rolling restarts, plus wait_for_evm_rpc_ready
  for the JSON-RPC attach lag.
- setup-kind.sh: delete stale clusters on fresh builds so containerd cannot
  serve an old image SHA under the new tag.

Carved from the closed grab-bag #210 (chore/e2e-kind-flake-fixes); the
log-to-stderr slice of that branch is #319 and is deliberately not included
here (no overlapping hunks; the two compose cleanly).

Validated end-to-end: full hardfork upgrade e2e (v1.3.0 -> current) 5/5 with
the new gates visible in the log, and the RPC suite 11/11 for framework
regression.

Signed-off-by: puneetmahajan <59960662+puneet2019@users.noreply.github.com>
puneet2019 added a commit that referenced this pull request Jul 9, 2026
… quorum

Root cause of the hardfork-upgrade flake ('Chain did not reach height 41
within 240s'): upgrade-chain.sh loaded the new-binary image with
'kind load docker-image ... 2>/dev/null || true' — a silent failure leaves
validators restarting into a missing/old image — and wait_for_chain_ready is
satisfied by a single healthy pod, so the harness declared 'chain resumed'
while 2/3 quorum was still absent. The post-upgrade height wait then burned
its whole budget measuring the wrong precondition.

- kind_load_image: docker save | ctr import into the kind node with explicit
  verification (crictl/ctr image listing), no silent-failure path; used by
  build-images.sh, framework.sh, and upgrade-chain.sh.
- wait_for_all_validator_rpcs: gate on EVERY validator serving its own RPC
  (real quorum signal) after rolling restarts, plus wait_for_evm_rpc_ready
  for the JSON-RPC attach lag.
- setup-kind.sh: delete stale clusters on fresh builds so containerd cannot
  serve an old image SHA under the new tag.

Carved from the closed grab-bag #210 (chore/e2e-kind-flake-fixes); the
log-to-stderr slice of that branch is #319 and is deliberately not included
here (no overlapping hunks; the two compose cleanly).

Validated end-to-end: full hardfork upgrade e2e (v1.3.0 -> current) 5/5 with
the new gates visible in the log, and the RPC suite 11/11 for framework
regression.

Signed-off-by: puneetmahajan <59960662+puneet2019@users.noreply.github.com>
puneet2019 added a commit that referenced this pull request Jul 13, 2026
… quorum

Root cause of the hardfork-upgrade flake ('Chain did not reach height 41
within 240s'): upgrade-chain.sh loaded the new-binary image with
'kind load docker-image ... 2>/dev/null || true' — a silent failure leaves
validators restarting into a missing/old image — and wait_for_chain_ready is
satisfied by a single healthy pod, so the harness declared 'chain resumed'
while 2/3 quorum was still absent. The post-upgrade height wait then burned
its whole budget measuring the wrong precondition.

- kind_load_image: docker save | ctr import into the kind node with explicit
  verification (crictl/ctr image listing), no silent-failure path; used by
  build-images.sh, framework.sh, and upgrade-chain.sh.
- wait_for_all_validator_rpcs: gate on EVERY validator serving its own RPC
  (real quorum signal) after rolling restarts, plus wait_for_evm_rpc_ready
  for the JSON-RPC attach lag.
- setup-kind.sh: delete stale clusters on fresh builds so containerd cannot
  serve an old image SHA under the new tag.

Carved from the closed grab-bag #210 (chore/e2e-kind-flake-fixes); the
log-to-stderr slice of that branch is #319 and is deliberately not included
here (no overlapping hunks; the two compose cleanly).

Validated end-to-end: full hardfork upgrade e2e (v1.3.0 -> current) 5/5 with
the new gates visible in the log, and the RPC suite 11/11 for framework
regression.

Signed-off-by: puneetmahajan <59960662+puneet2019@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants