Skip to content

bug(tests): most app suites give up on a HelmRelease before it can record why its install failed #3771

Closed

Description

Fourteen Chainsaw suites assert HelmRelease readiness at or below the install timeout of the release they are waiting on, so the assert always expires first and the reason the install failed is never reached. pkg/cmd/server/start.go:91 sets HelmReleaseInstallTimeout: "10m" for every release cozystack-api generates, no deployment passes --helmrelease-install-timeout, and only four ApplicationDefinitions override it (kubernetes at 20m, tenant, kubernetes-nodes and monitoring at 15m), none of which these suites use.

assert budget suite
5m external-dns, foundationdb, kuberture, openbao, qdrant
6m bucket, clickhouse, etcd, kafka, mongodb, postgres, redis, vminstance
10m harbor

The budget shown is the tightest HelmRelease-ready assert in each suite; several carry more than one. mariadb is excluded from the list because its budgets are the subject of a separate change.

The list is worth re-deriving rather than trusting, and the derivation is the point: walk every hack/e2e-chainsaw/*/chainsaw-test.yaml, find each kind: HelmRelease inside an assert:, take the nearest enclosing timeout:, and compare it against that release's floor. The neighbouring list, of the kubectl wait calls on HelmReleases elsewhere in hack/, resists being written down at all: a grep for the call misses the invocations split across lines, and reading for it by eye misses the single-release waits among the --all ones. The table below survives both traps because the asserts it walks are structurally uniform, but a reader adding a suite should re-run the walk rather than extend the rows.

Two suites look like the worst cases and are not cases at all. kubernetes-oidc-customconfig and kubernetes-oidc-system assert at 1m, but both wait on releases generated from the kubernetes ApplicationDefinition, which sets release.cozystack.io/helm-install-disable-wait: "true" (packages/system/kubernetes-rd/cozyrds/kubernetes.yaml:28). With the wait disabled the release goes Ready as soon as helm has applied, so 1m is a budget for an apply and is fine. kubernetes-nodes is the only other definition that disables it. Anyone sorting this list by budget will reach for those two first, which is why they are named here rather than left out.

harbor needs its own note, in the opposite direction: its parent release is not generated by cozystack-api but rendered by the app chart, which sets spec.timeout: 15m (packages/apps/harbor/templates/harbor.yaml:91). Its 10m assert is therefore five minutes under its real floor, not level with it as a comparison against the 600s default would suggest.

What this costs

When an install genuinely fails, Chainsaw reports its own timeout against a release that is still mid-install. The release records why the install failed when its own timeout fires, which is after the assert has already given up. The suite says the HelmRelease did not become Ready in six minutes, which is true and carries no information about why.

This is not a flake. These suites do not go red more often than they should; they go red just as often and say less when they do. Nothing here changes how many runs fail, only what a failed run can tell you.

I checked packages/apps/{clickhouse,redis,mongodb,postgres,foundationdb}/templates/ for a startupProbe and found none, so for those five there is no first-boot budget hiding under the wait either. I have not extended that check to the other nine, so whether any of them also needs headroom for its own bootstrap is open.

Why the ordering matters at all

helm-controller leaves spec.waitStrategy at its default of poller, so the install waits through kstatus, which falls through to a Ready-condition fallback for kinds it has no rules for and maps Ready=False onto InProgress. Any of these releases that renders a custom resource publishing a Ready condition therefore keeps the install running until that resource is healthy or the install's own budget is spent. The assert in front of it is sampling a release that is still working, and the only thing it can report is that the release had not finished.

Suggested shape

Whatever budget these asserts end up with should sit above the release's install timeout rather than at or below it, so a failed install has recorded its reason before the assert gives up.

One piece of prose has to move with the numbers. hack/e2e-chainsaw/mariadb/chainsaw-test.yaml:25 justifies its own budget as "11m rather than the usual 5-6m". That is descriptive of the table above and stops being true the moment this list is worked through, at which point it reads as an endorsement of the band it was contrasting against. Whoever raises these budgets owns that sentence.

The general form has two floors and a wait has to beat the larger. The first is the app's own first boot, which for a chart that sets startupProbe is initialDelaySeconds + (failureThreshold - 1) * periodSeconds and for a chart that does not is whatever its operator needs. The second is the install timeout, which is the binding floor whenever the first is smaller, and which is not one number: it is 600s by default, 900s or 1200s where an ApplicationDefinition overrides it, the chart's own spec.timeout for a release the chart renders, and absent entirely where the wait is disabled. Deriving it from the tree rather than pinning a literal is what keeps the relation true when either side moves; hack/mariadb-first-boot-waits.bats does that for one suite and is the shape to copy.

Activity

  1. lexfrei commented on Sep 22, 2026

    @lexfrei
    ContributorAuthor

    The backup examples have the same problem, and this issue does not cover them yet. The round-trip suites run examples/backups/*/run-all.sh, and its wait_hr_ready calls sit under the same install timeout as the Chainsaw asserts in the table.

    Line numbers are on origin/main at a75919087:

    The floor is the one this issue already uses. cozystack-api gives every release it generates a 10m install timeout (pkg/cmd/server/start.go:91). None of bucket-rd, mongodb-rd, rabbitmq-rd or postgres-rd sets helm-install-timeout or helm-install-disable-wait. So each of these waits gives up four or five minutes before the release can record why the install failed. Every copy of wait_hr_ready takes the budget as its second argument, so the fix is a number at each call site.

    The mariadb example's own mariadb waits (mariadb/run-all.sh:139 and :174) are not listed, because #3769 raises them to 660s. Its bucket wait at :52 still needs the fix.

    I left kafka out on purpose. examples/backups/kafka-metadata/run-all.sh:36 and :113 wait 300s, but packages/system/kafka-rd/cozyrds/kafka.yaml:15 sets helm-install-timeout: "7h" to cover the ZooKeeper to KRaft drain Job. No wait can get above that inside a CI job capped at 215 minutes. That needs its own decision, and it is the same one the kafka Chainsaw suite in the table needs.

  2. lexfrei commented on Sep 23, 2026

    @lexfrei
    ContributorAuthor

    Some waits under the same floor are missing from both the table and my earlier comment. Line numbers are on origin/main at 89da4a121.

    The table also misses these suites. Each one asserts HelmRelease readiness at 6m on a release that cozystack-api generates, and none of opensearch-rd, rabbitmq-rd or valkey-rd sets helm-install-timeout or helm-install-disable-wait:

    • hack/e2e-chainsaw/opensearch/chainsaw-test.yaml:128
    • hack/e2e-chainsaw/rabbitmq/chainsaw-test.yaml:27
    • hack/e2e-chainsaw/valkey/chainsaw-test.yaml:25

    In the backup examples I missed clickhouse. It calls wait_hr_ready from its numbered step scripts, not from run-all.sh:

    • bucket, 300s: examples/backups/clickhouse/03-create-bucket.sh:30
    • clickhouse, 300s: examples/backups/clickhouse/04-create-clickhouse.sh:57 and 07-restore-to-copy.sh:70

    Part of the example list is now handled elsewhere. #3769 raises the database waits in the clickhouse, mongodb and rabbitmq examples to 660s, same as mariadb, and grows each round-trip op by what those waits gained. After it lands, what stays under the floor in the examples is the bucket waits (clickhouse included) and the postgres waits already tracked in #3748. kafka stays out for the reason in my earlier comment.

    Both lists are easy to get wrong by hand, so here is how I built them. The loop prints every HelmRelease assert with its budget, and the grep prints every example wait with a literal budget:

    for f in hack/e2e-chainsaw/*/chainsaw-test.yaml; do
      awk -v f="$f" '
        /^[[:space:]]*-[[:space:]]+assert:/ { a = 1; t = ""; next }
        a && /^[[:space:]]*timeout:/        { t = $2; l = NR }
        a && /kind:[[:space:]]*HelmRelease/ { print f ":" l ": " t; a = 0 }
        /^[[:space:]]*-[[:space:]]+(script|apply|delete|error|wait|describe|events|podLogs|get|patch|create|update):/ { a = 0 }
      ' "$f"
    done
    grep -rnE 'wait_hr_ready "[^"]+" [0-9]+' examples/backups

    The loop also prints asserts that expect Ready to be False, like the rejected-upgrade check in the redis suite. Those are not readiness waits, skip them. Each remaining budget has to beat its release's own floor: 600s for a generated release unless its ApplicationDefinition overrides it, or the chart's spec.timeout for a release the chart renders itself.

  3. lexfrei commented on Sep 23, 2026

    @lexfrei
    ContributorAuthor

    A refreshed enumeration against main at 8d37e0b is in #4404, which I have closed as a duplicate of this issue. It lists 31 HelmRelease-ready asserts in 17 suites at or under their floor, the harbor pair (one on its 10m floor, one 5m under the chart's own 15m), the backup walkthrough waits, and a separate diagnostic gap in the redis and kafka-metadata wait_hr_ready helpers. #3769 has since merged: both mariadb asserts are at 11m, and the mariadb, mongodb, rabbitmq and clickhouse walkthrough database waits are at 660s with their Chainsaw ops grown to match. Everything else in that list is unchanged.

  4. added
    kind/bugCategorizes issue or PR as related to a bug
    area/testingIssues or PRs related to testing (e2e, bats, unit tests)
    triage/acceptedIndicates an issue is ready to be actively worked on
    priority/backlogGeneral backlog priority. Lower than priority/important-longterm
    and removed
    triage/needs-triageIndicates an issue needs triage by a maintainer
    on Sep 23, 2026
  5. added a commit that references this issue on Oct 2, 2026
    a7ac37b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/testingIssues or PRs related to testing (e2e, bats, unit tests)kind/bugCategorizes issue or PR as related to a bugpriority/backlogGeneral backlog priority. Lower than priority/important-longtermtriage/acceptedIndicates an issue is ready to be actively worked on

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions