Skip to content

test(e2e): make crust-gather failure snapshots reliable and complete - #2952

Closed
myasnikovdaniil wants to merge 1 commit into
mainfrom
ci/crust-gather-tenant-collect
Closed

myasnikovdaniil wants to merge 1 commit into
mainfrom
ci/crust-gather-tenant-collect

Conversation

@myasnikovdaniil

@myasnikovdaniil myasnikovdaniil commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

Follow-up to the crust-gather failure-snapshot logic on ci-next (#2948). On a failed e2e test, the EXIT trap in hack/cozytest.sh produced an incomplete, opaque dump — the tenant-cluster snapshot (the one that matters most, e.g. for a tenant HelmRelease stuck mid-install) silently went missing. Three causes:

  • the collect's stdout/stderr/rc were sent to /dev/null, so a failed collect left no trace of why;
  • the tenant kubeconfig points at https://localhost:<port> served by a kubectl port-forward backgrounded minutes earlier — usually dead by the time the trap runs, so crust-gather couldn't reach the tenant API;
  • Helm release storage (kind Secret) was dropped along with every other Secret, so the dump couldn't show a release's revision history / status.

This makes the gather reliable and complete:

  • Visible failures — each collect writes a sibling <target>.collect.log (stdout + stderr + rc) and prints a per-target ✓/✗ status, so a failed collect leaves a breadcrumb in the artifact instead of vanishing.
  • Reachable tenant — probe /readyz; if unreachable, re-establish the port-forward from the kubeconfig (name + server port) before collecting.
  • Bounded — wrap every collect in timeout 5m so a wedged forward can't stall the job.
  • Helm storage — a dedicated include-only collect (host + each tenant) captures just the sh.helm.release.* secrets. Verified from crust-gather v0.15.2 source: all CLI filters land in one FilterList evaluated with .all(), so --include-kind Secret --include-name <re> keeps a Secret only when both match.

Scope is limited to hack/cozytest.sh, which runs only in the e2e harness — no production code paths are touched.

Release note

NONE

Summary by CodeRabbit

  • Bug Fixes
    • Improved reliability of failure snapshot diagnostics, with clearer status reporting (including partial vs failed outcomes).
    • Captures full snapshot output into dedicated log files instead of suppressing it.
    • Ensures bounded snapshot collection runs and, on failures, saves both standard resources and Helm release Secrets in separate snapshots for easier analysis.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the reliability and completeness of e2e failure snapshots in the crust-gather process. By addressing issues with silent collection failures, unreachable tenant APIs, and missing Helm release data, the changes ensure that debugging information is consistently available when tests fail. The scope is strictly limited to the e2e test harness, improving diagnostic capabilities without impacting production code.

Highlights

  • Improved Failure Diagnostics: Added logging for crust-gather operations, ensuring stdout, stderr, and exit codes are captured in .collect.log files to prevent silent failures.
  • Reliable Tenant Connectivity: Implemented automatic re-establishment of port-forwarding for tenant clusters if the API is unreachable during the exit trap.
  • Helm Release Visibility: Introduced a dedicated collection step for Helm release secrets, ensuring critical release history and status are preserved in snapshots.
  • Execution Safety: Wrapped collection tasks with a 5-minute timeout to prevent the test harness from stalling due to unresponsive port-forwards.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment Gemini (@gemini-code-assist) Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@coderabbitai

coderabbitai Bot commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: efcfa111-1d02-48cd-9249-1125c38c7815

📥 Commits

Reviewing files that changed from the base of the PR and between 00ed99d and bfaac1e.

📒 Files selected for processing (1)
  • hack/cozytest.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • hack/cozytest.sh

📝 Walkthrough

Walkthrough

hack/cozytest.sh adds bounded snapshot collection helpers and changes failure handling to save host resources in two passes: non-Secret data and Helm release Secrets.

Changes

Failure Snapshot Diagnostics

Layer / File(s) Summary
Collection helpers
hack/cozytest.sh
Adds _cozy_collect to run crust-gather collect under timeout, write dest.collect.log, and classify results as PARTIAL or FAILED, plus _cozy_collect_helm for Helm release Secret collection.
_cozy_on_exit failure capture
hack/cozytest.sh
Replaces the prior HOST snapshot call with two helper-driven captures: HOST excluding Secrets and HOST Helm release Secrets.

Sequence Diagram(s)

sequenceDiagram
  participant on_exit as _cozy_on_exit
  participant collect as _cozy_collect
  participant gather as crust-gather collect
  participant disk as snapshot artifacts

  on_exit->>collect: collect host non-Secret resources
  collect->>gather: run with timeout and filters
  gather->>disk: write tree and dest.collect.log

  on_exit->>collect: collect host Helm release Secrets
  collect->>gather: run with Helm filters
  gather->>disk: write tree and dest.collect.log
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 I hop through logs with tidy feet,
Two snapshots make the story neat.
Secrets and Helm both get their say,
In *.collect.log they stay.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the e2e snapshot reliability and completeness changes in hack/cozytest.sh.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/crust-gather-tenant-collect

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added area/testing Issues or PRs related to testing (e2e, bats, unit tests) size/M This PR changes 30-99 lines, ignoring generated files labels Jun 19, 2026
@dosubot dosubot Bot added the area/ci Issues or PRs related to CI workflows, GitHub Actions, automation label Jun 19, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enhances the E2E test runner script hack/cozytest.sh by improving the collection of diagnostic snapshots when tests fail. It introduces helper functions _cozy_collect and _cozy_collect_helm to capture standard logs and Helm release secrets, and adds logic to automatically re-establish dead port-forwards to tenant clusters during cleanup. The review feedback suggests declaring several variables as local within these new functions and the exit trap handler to prevent global variable pollution and dynamic scoping issues.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread hack/cozytest.sh Outdated
Comment on lines +103 to +105
_cozy_collect() {
_label=$1; _dest=$2; shift 2
if timeout 5m crust-gather collect -f "$_dest" "$@" >"$_dest.collect.log" 2>&1; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The variables _label and _dest are currently assigned globally. Since _cozy_collect_helm also uses these same variable names and calls _cozy_collect, this creates a global variable shadowing/overwriting risk. Declaring them as local prevents any dynamic scoping issues or accidental overwrites.

Suggested change
_cozy_collect() {
_label=$1; _dest=$2; shift 2
if timeout 5m crust-gather collect -f "$_dest" "$@" >"$_dest.collect.log" 2>&1; then
_cozy_collect() {
local _label=$1 _dest=$2; shift 2
if timeout 5m crust-gather collect -f "$_dest" "$@" >"$_dest.collect.log" 2>&1; then

Comment thread hack/cozytest.sh
Comment on lines +119 to +121
_cozy_collect_helm() {
_label=$1; _dest=$2; shift 2
_cozy_collect "$_label" "$_dest" \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similarly, _label and _dest in _cozy_collect_helm should be declared as local to avoid polluting the global scope and prevent dynamic scoping bugs.

Suggested change
_cozy_collect_helm() {
_label=$1; _dest=$2; shift 2
_cozy_collect "$_label" "$_dest" \
_cozy_collect_helm() {
local _label=$1 _dest=$2; shift 2
_cozy_collect "$_label" "$_dest" \\

Comment thread hack/cozytest.sh Outdated
Comment on lines 125 to 127
_cozy_on_exit() {
_rc=$?
if [ "$_rc" -ne 0 ] && command -v crust-gather >/dev/null 2>&1; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In _cozy_on_exit, several variables (_snap, _kc, _name, _srv, _port, _pf_pid) are used globally. Since _cozy_on_exit is registered as an EXIT trap handler, it is highly recommended to declare these variables as local to avoid polluting the global environment or accidentally overwriting variables of the same name in the main script or BATS tests.

Suggested change
_cozy_on_exit() {
_rc=$?
if [ "$_rc" -ne 0 ] && command -v crust-gather >/dev/null 2>&1; then
_cozy_on_exit() {
_rc=$?
local _snap _kc _name _srv _port _pf_pid
if [ "$_rc" -ne 0 ] && command -v crust-gather >/dev/null 2>&1; then

Base automatically changed from ci-next to main June 22, 2026 04:19
@myasnikovdaniil myasnikovdaniil self-assigned this Jun 23, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@hack/cozytest.sh`:
- Around line 150-151: Between the pkill command that terminates the existing
port-forward process and the kubectl port-forward command that starts a new one,
add a brief sleep delay to allow the operating system to fully release the TCP
port. This prevents the intermittent "address already in use" binding failure
that occurs when port-forward attempts to bind immediately after pkill returns,
as pkill does not wait for the process to fully release system resources.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 514583ab-17bc-4dd2-8646-21cb411d2108

📥 Commits

Reviewing files that changed from the base of the PR and between aeaedca and a234a61.

📒 Files selected for processing (1)
  • hack/cozytest.sh

Comment thread hack/cozytest.sh Outdated
@myasnikovdaniil
myasnikovdaniil force-pushed the ci/crust-gather-tenant-collect branch from b47b66a to 00ed99d Compare June 24, 2026 05:05

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (APPROVE)

Business context: improves the on-failure crust-gather snapshot in the e2e runner so a failed collect leaves a legible breadcrumb instead of vanishing into /dev/null, bounds each collect with a timeout, and captures the Helm release Secrets that the blanket --exclude-kind Secret otherwise drops.

I verified the shell correctness on the head and cross-checked it with a second model. Findings:

  • The else-branch status capture is correct: _crc=$? is the first statement after else, so it still holds the failed timeout/crust-gather status rather than the if-compound's 0. Evidence: hack/cozytest.sh:115-118.
  • The collect helpers always return 0 (a trailing echo is the last command in every branch), so under set -e the EXIT trap proceeds to cozy_cleanup exactly as before — no regression in exit-status handling, and no risk of the trap aborting early on a failed collect. Evidence: hack/cozytest.sh:110-125, trap registration at hack/cozytest.sh:163.
  • The dedicated Helm-storage collect relies on crust-gather composing --include-kind and --include-name as a single AND-evaluated FilterList, so it keeps only sh.helm.release.* Secrets. Evidence: hack/cozytest.sh:134-138.
  • No new retry loop and no new trap — the existing runner-level EXIT trap is modified in place, and the only added control flow is timeout-bounding. This is consistent with the e2e conventions (fail-fast, no deterministic-step retries, diagnostics-first). Evidence: hack/cozytest.sh:140-156; docs/agents/e2e-testing.md sections 1, 3, 8.

No blockers.

Non-blocking:

  1. The description overpromises relative to the delivered diff. The body describes a tenant /readyz probe, port-forward re-establishment from the kubeconfig, and a per-tenant Helm collect ("host + each tenant"), but the head only enhances the host snapshot: a host collect plus a host-helm collect. There is no tenant probing, no port-forward recovery, and no per-tenant capture in this change, so the opening framing ("the tenant-cluster snapshot — the one that matters most — silently went missing") is not addressed here. Please align the body with the actual host-only scope, or call out the tenant work as a follow-up. Evidence: hack/cozytest.sh:140-156 contains only the host and host-helm collects; git diff origin/main...HEAD touches hack/cozytest.sh only, with no port-forward or tenant logic.

cozytest.sh's parent-shell EXIT trap snapshots the HOST cluster on a failed
test (nested tenant clusters are captured separately, by run-kubernetes.sh's
in-subshell trap, while their port-forward is still alive). That host
snapshot had three gaps: the collect's output went to /dev/null so a failed
collect left no clue why; Helm release storage (kind Secret) was dropped
along with every other Secret, losing the revision history that explains a
HelmRelease stuck mid-install; and the node host-log scanner burned the
collect's --duration deadline on debug pods PodSecurity can never admit,
forcing a non-zero exit even though the API tree was already on disk.

- factor the collect into a _cozy_collect helper that captures
  stdout/stderr/rc to a sibling .collect.log and prints a per-target status,
  so a failed collect leaves evidence in the artifact instead of vanishing;
  bound it with `timeout -k 30 5m` so a degraded cluster can't stall the job
- classify a non-zero collect that still wrote a resource tree as PARTIAL
  rather than FAILED, so a usable snapshot isn't discarded over the label
  (capture $? as the first statement in the else — after `fi` it would be
  the if-compound's 0, not the collect's exit code)
- add a dedicated include-only collect (_cozy_collect_helm) for Helm release
  secrets (sh.helm.release.*), so the artifact carries each release's
  revision history and status
- pass --disable-additional-logs so the host-log scanner doesn't burn the
  deadline on debug pods that can never be admitted; container/pod logs come
  from a separate path and are unaffected, and Talos host logs aren't
  reachable that way anyway

crust-gather puts all CLI filters in one FilterList evaluated with .all(),
so `--include-kind Secret --include-name <re>` keeps a Secret only when both
match — verified from source.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>

@IvanHunters IvanHunters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve — the code is correct and safe on the failure path.

Verified: _crc=$? captures the failed collect status, both helpers end in an echo (return 0) so they can't abort the trap under set -eu, every collect is bounded by timeout -k 30 5m, and the snapshot step never alters the exit code or masks a real failure. shellcheck clean.

Non-blocking: the PR body describes tenant /readyz probing, port-forward re-establishment, and per-tenant helm capture that are not in this diff — the change is host-only, and the tenant collect in run-kubernetes.sh is a separate, untouched trap. Worth trimming the body to the actual scope or marking the rest as follow-up. The bot suggestions don't apply here: local is non-portable in this POSIX sh file (the file uses the _-prefixed global convention), and the port-forward sleep targets code that no longer exists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/ci Issues or PRs related to CI workflows, GitHub Actions, automation area/testing Issues or PRs related to testing (e2e, bats, unit tests) size/M This PR changes 30-99 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants