Skip to content

fix(e2e): drive LINSTOR post-install waits off a single 15m deadline - #2928

Closed
myasnikovdaniil wants to merge 1 commit into
ci/build-ephemeral-runnerfrom
fix/e2e-postprep-timeout
Closed

fix(e2e): drive LINSTOR post-install waits off a single 15m deadline#2928
myasnikovdaniil wants to merge 1 commit into
ci/build-ephemeral-runnerfrom
fix/e2e-postprep-timeout

Conversation

@myasnikovdaniil

@myasnikovdaniil myasnikovdaniil commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

What this PR does

hack/e2e-post-install-prep.sh waited for each LINSTOR prerequisite with a separate fixed timeout anchored to the script's start (timeout 60 for the linstor HelmRelease to exist, timeout 300 for the controller Deployment, timeout 60 for nodes Online). Those budgets race the platform reconcile chain. The cozystack-operator alone needs ~70s just to emit the linstor HR, so the 60s exists-wait expired ~10s before the object appeared and set -e aborted the whole script — the install failed with a single log line and no diagnostics. This is what broke E2E on the v1.5.0-rc.3 release-cut PR (#2924); that run's platform actually converged at the normal ~10:40 mark.

Across recent CI runs healthy LINSTOR convergence is ~11–13 min, so the per-hop guesses had no margin and only passed on favourable variance.

This replaces the exists+ready pairs with a wait_for helper that polls kubectl wait under one shared 15m deadline — the same window the installer's kubectl wait hr --all already uses — tolerating not-yet-created objects (kubectl wait exits non-zero on NotFound) without the set -e cliff, while still failing hard if a resource never becomes ready inside the budget. The 3-nodes-Online wait goes 60s → 300s for the same reason.

Verified by running the edited script end-to-end under a fake kubectl: slow-then-ready completes; never-ready fails at the deadline; a late HelmRelease (the rc.3 trigger) is now tolerated.

Release note

NONE

Summary by CodeRabbit

  • Refactor
    • Streamlined e2e readiness waiting with a single shared deadline.
    • Unified Docker build caching using a shared cache-args mechanism (optional cache writes).
  • New Features
    • Added an in-cluster watchdog to automatically heal e2e Cilium endpoint leaks.
    • Extended SeaweedFS COSI templates to support additional bucket classes and compliance object lock.
  • CI / Build Improvements
    • Warmed main-branch Docker build cache; use ephemeral runners by default and ensure build tooling is available.
  • Bug Fixes
    • Improved core component health checking with a startup probe and updated e2e bucket connectivity checks.

@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 improves the reliability of the LINSTOR post-install preparation script by addressing race conditions between the script's execution and the platform's reconcile chain. By moving to a shared deadline and a more robust polling mechanism, the script now correctly handles delayed resource creation without failing, significantly reducing flaky E2E test failures.

Highlights

  • Shared Deadline Implementation: Replaced individual, fixed-duration timeouts with a unified 15-minute deadline for all LINSTOR prerequisite checks to better accommodate reconcile latencies in CI.
  • Resilient Wait Logic: Introduced a wait_for helper function that polls kubectl wait and gracefully handles 'NotFound' errors, preventing the script from aborting prematurely when resources are not yet created.
  • Increased Node Readiness Timeout: Extended the timeout for waiting on 3 LINSTOR nodes to come online from 60 seconds to 300 seconds to improve stability under load.
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.

@github-actions github-actions Bot added kind/bug Categorizes issue or PR as related to a bug size/M This PR changes 30-99 lines, ignoring generated files labels Jun 16, 2026
@myasnikovdaniil myasnikovdaniil added the backport Should change be backported on previous release label Jun 16, 2026
@dosubot dosubot Bot added the area/ci Issues or PRs related to CI workflows, GitHub Actions, automation label Jun 16, 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 refactors the hack/e2e-post-install-prep.sh script to use a shared 15-minute deadline for waiting on LINSTOR prerequisites instead of individual, racing timeouts. It introduces a wait_for helper function to handle resource non-existence gracefully without triggering set -e aborts. Feedback on these changes includes declaring variables as local within the wait_for function to prevent global scope leakage, and wrapping the timeout 300 command for LINSTOR nodes in a conditional block to print a clear error message upon timeout instead of silently aborting.

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.


echo "[post-install-prep] waiting for 3 LINSTOR nodes Online"
timeout 60 sh -ec 'until [ $(kubectl exec -n cozy-linstor deploy/linstor-controller -- linstor node list | grep -c Online) -eq 3 ]; do sleep 1; done'
timeout 300 sh -ec 'until [ $(kubectl exec -n cozy-linstor deploy/linstor-controller -- linstor node list | grep -c Online) -eq 3 ]; do sleep 2; done'

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

If the timeout 300 command times out, it will exit with status 124. Because set -e is active, the script will abort immediately without printing any error message, which can make debugging CI failures difficult. Wrapping it in an if statement allows us to print a clear error message to stderr before exiting.

Suggested change
timeout 300 sh -ec 'until [ $(kubectl exec -n cozy-linstor deploy/linstor-controller -- linstor node list | grep -c Online) -eq 3 ]; do sleep 2; done'
if ! timeout 300 sh -ec 'until [ $(kubectl exec -n cozy-linstor deploy/linstor-controller -- linstor node list | grep -c Online) -eq 3 ]; do sleep 2; done'; then
echo "[post-install-prep] timed out waiting for 3 LINSTOR nodes Online" >&2
exit 1
fi

Comment on lines +30 to +35
wait_for() {
desc=$1
shift
echo "[post-install-prep] waiting for ${desc}"
while :; do
remaining=$(( DEADLINE - $(date +%s) ))

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.

low

Declaring variables as local inside functions is a best practice in shell scripting to prevent them from leaking into the global scope and causing unexpected side effects, especially in larger scripts.

Suggested change
wait_for() {
desc=$1
shift
echo "[post-install-prep] waiting for ${desc}"
while :; do
remaining=$(( DEADLINE - $(date +%s) ))
wait_for() {
local desc=$1
shift
echo "[post-install-prep] waiting for ${desc}"
while :; do
local remaining=$(( DEADLINE - $(date +%s) ))

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

Failed to post review comments

📝 Walkthrough

Walkthrough

This PR spans build infrastructure, CI workflows, E2E test hardening, and storage configuration improvements. It introduces a centralized Docker Buildx cache macro applied across all image builds, adds a dedicated cache-warming job for the main branch, refactors e2e post-install waits around a shared deadline, implements in-cluster Cilium endpoint-leak healing, and updates SeaweedFS storage classes and volume sizing.

Changes

Build cache foundation and CI integration

Layer / File(s) Summary
Cache-args macro and configuration defaults
hack/common-envs.mk
Introduces CACHE_REGISTRY default as $(REGISTRY) (colocated with build registry), WRITE_CACHE flag (default 0) to gate cache writes, and cache-args make macro that always emits --cache-from and conditionally emits --cache-to (when WRITE_CACHE=1) with type=registry, mode=max, and OCI manifest settings.
CI warm-cache job for main branch
.github/workflows/build-main.yaml
Adds warm-cache job triggered on pushes to main (excluding docs/**), uses serialized concurrency to allow only the newest build to write cache, authenticates to OCIR, configures Docker Buildx with docker-container driver, and runs make build with WRITE_CACHE=1, IMAGE_TAG=main, and publication disabled.
Ephemeral runner and build toolchain setup
.github/workflows/pull-requests.yaml
Configures pull request builds to default to ephemeral Oracle VM runner (oracle-vm-4cpu-16gb-x86-64), falling back to self-hosted only when the debug label is present; adds Set up build toolchain step that conditionally installs helm-unittest plugin and flux CLI when missing.

E2E test infrastructure hardening

Layer / File(s) Summary
Shared deadline and wait_for helper
hack/e2e-post-install-prep.sh
Introduces DEADLINE (900 seconds from script start) and a wait_for shell function that repeatedly runs kubectl wait with remaining-budget timeout, suppresses not-yet-created errors, and returns non-zero only when deadline elapses.
HelmRelease and Deployment readiness waits
hack/e2e-post-install-prep.sh
Replaces separate object-exists polling and fixed-time kubectl wait calls for LINSTOR HelmRelease Ready and linstor-controller Deployment available with two wait_for invocations; extends LINSTOR nodes Online loop to 300s timeout with 2s polling interval.
Cilium endpoint-leak healer script
hack/e2e-cilium-endpoint-leak-healer.sh
Implements continuous event-driven healer watching Kubernetes Events for FailedCreatePodSandBox "IP ipv4: is already in use" failures, extracts disputed IPv4s, verifies pod/node state, queries cilium-agent endpoints, determines orphan status, and conditionally disconnects via cilium-dbg endpoint disconnect with configurable poll interval and namespace.
Cilium healer Kubernetes manifest and RBAC
hack/e2e-cilium-leak-healer.yaml
Defines ServiceAccount, ClusterRole (read/watch events, read/list pods, pods/exec), ClusterRoleBinding in kube-system, and a Job with hostNetwork, broad tolerations, OnFailure restart, and high backoffLimit that runs the healer script via ConfigMap at /opt/healer.
Healer integration into test suite and bucket test fixes
hack/e2e-install-cozystack.bats, hack/e2e-apps/bucket.bats
Adds setup_file() to create cilium-leak-healer ConfigMap and apply healer manifest; adds teardown_file() to surface healer activity (HEAL/REFUSE log lines) into test output; updates bucket.bats port-forward to use backgrounded kubectl directly and probe/alias 127.0.0.1:8333 instead of localhost.

Docker Buildx cache adoption across Makefiles

Layer / File(s) Summary
Cache adoption in packages/apps Makefiles
packages/apps/clickhouse/Makefile, packages/apps/http-cache/Makefile, packages/apps/kubernetes/Makefile, packages/apps/mariadb/Makefile
All docker buildx image builds switch from explicit --cache-from/--cache-to inline cache flags to $(call cache-args,<image>) invocations for consistent centralized cache behavior.
Cache adoption in packages/core Makefiles
packages/core/installer/Makefile, packages/core/platform/Makefile, packages/core/talos/Makefile, packages/core/testing/Makefile
Core package image builds update docker buildx cache configuration to use cache-args macro.
Cache adoption in packages/extra and system backup Makefiles
packages/extra/monitoring/Makefile, packages/system/backup-controller/Makefile, packages/system/backupstrategy-controller/Makefile
Extra and system backup controller image builds switch to centralized cache-args macro.
Cache adoption in packages/system miscellaneous Makefiles
packages/system/bucket/Makefile, packages/system/cilium/Makefile, packages/system/cozystack-api/Makefile, packages/system/cozystack-controller/Makefile
System miscellaneous package image builds update cache configuration to use cache-args macro.
Cache adoption in packages/system dashboard Makefiles
packages/system/dashboard/Makefile
Both image-console (cozystack-ui) and image-token-proxy (token-proxy) builds switch to cache-args macro.
Cache adoption in packages/system network and operator Makefiles
packages/system/flux-plunger/Makefile, packages/system/grafana-operator/Makefile, packages/system/kamaji/Makefile, packages/system/keycloak-operator/Makefile, packages/system/kilo/Makefile, packages/system/kubeovn-plunger/Makefile, packages/system/kubeovn-webhook/Makefile, packages/system/lineage-controller-webhook/Makefile, packages/system/linstor-gui/Makefile, packages/system/linstor/Makefile, packages/system/metallb/Makefile
Network and operator package builds update docker buildx cache configuration to use cache-args macro; some also add conditional --builder flags.
Cache adoption in remaining system Makefiles
packages/system/monitoring/Makefile, packages/system/multus/Makefile, packages/system/objectstorage-controller/Makefile, packages/system/redis-operator/Makefile
Remaining system package image builds switch to centralized cache-args macro for cache configuration.

SeaweedFS storage and health configuration

Layer / File(s) Summary
COSI bucket and access classes for object lock
packages/system/seaweedfs/charts/seaweedfs/templates/cosi/cosi-bucket-class.yaml
Adds BucketClass with -lock suffix, deletionPolicy: Retain, and COMPLIANCE object lock for 365 days; updates primary BucketAccessClass with parameters.accessPolicy: readwrite; adds new -readonly BucketAccessClass with parameters.accessPolicy: readonly.
S3 service metadata name update
packages/system/seaweedfs/charts/seaweedfs/templates/s3/s3-service.yaml
Changes Service metadata.name generation from seaweedfs.componentName to seaweedfs.name helper with -s3 suffix, affecting service resource naming when S3 is enabled.
SeaweedFS volume sizing adjustment
packages/system/seaweedfs/values.yaml
Reduces volumeSizeLimitMB from 30000 to 1000 with documentation explaining volume slot assumptions and default bucket consumption impact on test sandbox PVC.
CAPI startup health probe
packages/system/capi-providers-core/files/core-components.yaml
Adds startupProbe performing HTTP GET to /healthz on healthz port with failureThreshold: 30 and periodSeconds: 10 for improved pod startup observability.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • cozystack/cozystack#2855: Introduces CACHE_REGISTRY configuration to redirect build cache away from default registry; this PR builds on that foundation by centralizing cache behavior across all Makefiles via the new cache-args macro and adding a dedicated cache-warming CI job.

Suggested reviewers

  • kvaps
  • androndo
  • sircthulhu
  • lllamnyp

Poem

🐇 A rabbit's cache warms while builders toil,
E2E healers soothe Cilium's roil,
Buckets locked tight, volumes downsized just right,
One deadline shared keeps the e2e night bright! ✨

🚥 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 PR title directly reflects the main change: consolidating LINSTOR post-install waits under a single 15-minute deadline instead of multiple separate timeouts.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 fix/e2e-postprep-timeout

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 and usage tips.

@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: 2

🤖 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/e2e-post-install-prep.sh`:
- Line 40: The kubectl wait command on line 40 of the e2e-post-install-prep.sh
script suppresses all stderr output by redirecting to /dev/null, which hides not
only expected NotFound errors but also authentication failures, invalid syntax,
and network errors, making debugging impossible. Remove the stderr redirection
(2>/dev/null) from the kubectl wait command entirely. This will allow all error
messages to be displayed in the logs, making it clear what the script is waiting
for and immediately surfacing any genuine misconfiguration issues, while the
script will still continue retrying based on the exit code alone.
- Line 50: The kubectl wait command uses a lowercase condition name in the
--for=condition=available parameter, but Kubernetes condition types are
case-sensitive and require Available with a capital A. Update the condition
parameter from --for=condition=available to --for=condition=Available to match
the correct Kubernetes Deployment condition type, preventing the kubectl wait
command from failing immediately during the e2e test execution.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: ff978f35-3d90-4432-ae46-9fc445406dbc

📥 Commits

Reviewing files that changed from the base of the PR and between 8a23f84 and 9e0a6d1.

📒 Files selected for processing (1)
  • hack/e2e-post-install-prep.sh

echo "[post-install-prep] timed out waiting for ${desc}" >&2
return 1
fi
if kubectl wait "$@" --timeout="${remaining}s" 2>/dev/null; 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.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Silencing all stderr makes debugging impossible.

Line 40 redirects all kubectl stderr to /dev/null to tolerate NotFound errors, but this also suppresses authentication failures, invalid syntax, network errors, and any other genuine problem. When a prerequisite fails to appear, operators will see the script retry silently for 15 minutes with no diagnostic output.

Consider one of these approaches:

  • Capture stderr and only suppress lines containing "NotFound" or "no matching resources"
  • Run kubectl get <resource> first to distinguish "doesn't exist yet" from other errors
  • Let stderr through and rely on exit code alone (kubectl wait already retries internally)

The third option is simplest: remove 2>/dev/null entirely, accept that "NotFound" messages will appear in the log (which is actually helpful for understanding timing), and let the loop continue regardless.

Suggested approach: remove stderr suppression
-    if kubectl wait "$@" --timeout="${remaining}s" 2>/dev/null; then
+    if kubectl wait "$@" --timeout="${remaining}s"; then
       return 0
     fi

This will log all errors (including expected "NotFound" during startup), making it clear what the script is waiting for and surfacing any genuine misconfiguration immediately.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if kubectl wait "$@" --timeout="${remaining}s" 2>/dev/null; then
if kubectl wait "$@" --timeout="${remaining}s"; then
🤖 Prompt for 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.

In `@hack/e2e-post-install-prep.sh` at line 40, The kubectl wait command on line
40 of the e2e-post-install-prep.sh script suppresses all stderr output by
redirecting to /dev/null, which hides not only expected NotFound errors but also
authentication failures, invalid syntax, and network errors, making debugging
impossible. Remove the stderr redirection (2>/dev/null) from the kubectl wait
command entirely. This will allow all error messages to be displayed in the
logs, making it clear what the script is waiting for and immediately surfacing
any genuine misconfiguration issues, while the script will still continue
retrying based on the exit code alone.

wait_for "linstor HelmRelease to be Ready" \
helmrelease/linstor -n cozy-linstor --for=condition=Ready
wait_for "linstor-controller Deployment to be Available" \
deployment/linstor-controller -n cozy-linstor --for=condition=available

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.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify kubectl wait accepts lowercase "available" for Deployment conditions

# Check if kubectl wait --help documents case requirements
kubectl wait --help 2>&1 | grep -A5 "condition="

# Attempt to validate the condition syntax (will fail on cluster without the deployment, but syntax errors surface immediately)
kubectl wait deployment/nonexistent -n default --for=condition=available --timeout=1s 2>&1 | head -20

Repository: cozystack/cozystack

Length of output: 109


🏁 Script executed:

cat -n hack/e2e-post-install-prep.sh | sed -n '45,55p'

Repository: cozystack/cozystack

Length of output: 678


🌐 Web query:

kubectl wait condition names case sensitivity Available kubernetes

💡 Result:

When using the kubectl wait command, the behavior regarding case sensitivity depends on what part of the --for parameter you are referencing. 1. Condition Values: When specifying the status value of a condition (e.g., --for=condition=Ready=true), the comparison is performed after Unicode simple case folding [1][2][3]. This is a form of case-insensitivity that allows for more flexible matching of the status value [4][5]. 2. Condition Names: The actual condition names (the type, such as Ready, PodScheduled, or Initialized) are case-sensitive [6]. These names must match the condition type defined in the resource's status exactly as it appears in the Kubernetes API [6]. Using incorrect casing (e.g., using --for=condition=ready instead of --for=condition=Ready) will result in the command failing to find the expected condition, often causing it to exit immediately or wait indefinitely if the condition name is not recognized [6]. Therefore, while the status value is evaluated with case-insensitive logic, you must always provide the correct, case-sensitive condition type name as defined by the Kubernetes API for the resource you are monitoring [6].

Citations:


🌐 Web query:

Kubernetes Deployment condition names Available capital

💡 Result:

In Kubernetes, Available is one of the standard condition types for a Deployment [1][2]. It is reported within the status.conditions array of a Deployment resource [1][3]. The Available condition indicates whether the Deployment has reached its minimum required availability [1][4]. Specifically, a status of True means that the Deployment has the minimum number of replicas available as required by the deployment strategy [1][2]. Key details regarding the Available condition: - Type: Available [3][2] - Status: True, False, or Unknown [3][2] - Reasons: Common reasons include MinimumReplicasAvailable (when status is True) and MinimumReplicasUnavailable (when status is False) [1][5][2]. - Calculation: The condition considers whether the number of available pods meets the minimum threshold defined by the deployment strategy (often influenced by maxUnavailable) [1][4]. A pod is considered available if it is ready for at least minReadySeconds [4][6]. Other standard Deployment conditions include Progressing and ReplicaFailure [2]. All deployment conditions follow the same schema, including fields such as lastTransitionTime, lastUpdateTime, message, reason, status, and type [3][2].

Citations:


Change --for=condition=available to --for=condition=Available.

The Kubernetes Deployment condition type is case-sensitive and must be Available with a capital A. Using lowercase available will cause kubectl wait to fail immediately, breaking the e2e test flow.

🤖 Prompt for 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.

In `@hack/e2e-post-install-prep.sh` at line 50, The kubectl wait command uses a
lowercase condition name in the --for=condition=available parameter, but
Kubernetes condition types are case-sensitive and require Available with a
capital A. Update the condition parameter from --for=condition=available to
--for=condition=Available to match the correct Kubernetes Deployment condition
type, preventing the kubectl wait command from failing immediately during the
e2e test execution.

The post-install-prep helper waited for each LINSTOR prerequisite with a
separate fixed timeout anchored to the script's start: timeout 60 for the
linstor HelmRelease object to exist, timeout 300 for the controller
Deployment, timeout 60 for nodes Online. Those budgets raced the platform
reconcile chain. The cozystack-operator alone needs ~70s just to emit the
linstor HR, so the 60s exists-wait expired ~10s before the object appeared
and 'set -e' aborted the whole script, failing the install with a single
log line and no diagnostics. Across recent CI runs healthy LINSTOR
convergence is ~11-13 min, so the per-hop guesses had no margin and passed
only on favourable variance.

Replace the exists+ready pairs with a wait_for helper that polls
'kubectl wait' under one shared 15m deadline -- the same window the
installer's 'kubectl wait hr --all' already uses -- tolerating
not-yet-created objects (kubectl wait exits non-zero on NotFound) without
the set -e cliff, while still failing hard if a resource never becomes
ready inside the budget. Bump the 3-nodes-Online wait 60s to 300s for the
same reason.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
@myasnikovdaniil
myasnikovdaniil force-pushed the fix/e2e-postprep-timeout branch from 9e0a6d1 to 6ce1404 Compare June 17, 2026 16:03
@github-actions github-actions Bot added size/XL This PR changes 500-999 lines, ignoring generated files and removed size/M This PR changes 30-99 lines, ignoring generated files labels Jun 17, 2026
@myasnikovdaniil
myasnikovdaniil changed the base branch from main to ci/build-ephemeral-runner June 17, 2026 16:03
myasnikovdaniil added a commit that referenced this pull request Jun 18, 2026
…t budgets

kubernetes-latest flaked because the worker-readiness wait was split into 8m-to-join + 3m-to-become-Ready. Under sandbox load a slow KubeVirt VM boot consumes most of the join budget, then the tenant cluster's cilium CNI needs several more minutes to make the freshly-joined nodes Ready, overflowing the fixed 3m Ready window even though the CNI converges fine (its HelmRelease reached True shortly after). Replace with one 12m deadline polling for >=2 Ready nodes, robust to wherever the time goes. Same single-deadline shape as the LINSTOR post-install fix (#2928).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
@myasnikovdaniil
myasnikovdaniil marked this pull request as draft June 19, 2026 08:47
myasnikovdaniil added a commit that referenced this pull request Jun 22, 2026
## What

Consolidated landing branch for a set of interdependent platform/CI
stabilization fixes. It began as the source PRs in the table below (in
dependency order) and has since grown with review-driven fixes and a few
production-behavior changes — the full current inventory is under
**Scope** below. Landing them as one unit lets CI run against the real
combined state instead of a fragile bottom-up merge train.

## Source PRs (dependency order)

| # | Commit | Source PR | Addresses |
|---|--------|-----------|-----------|
| 1 | fix(capi): startupProbe on capi-controller-manager | #2946 | capi
crashloop during cert provisioning |
| 2 | test(e2e): bucket.bats port-forward + S3 client reliability |
#2944 | flaky bucket test |
| 3 | fix(seaweedfs): restore -lock BucketClass, s3 svc name, drop
volumeSizeLimitMB | #2943 | missing bucket/access classes |
| 4 | test(e2e): in-cluster Cilium endpoint-leak healer (install + apps)
| #2874 | cilium "IP already in use" leak |
| 5 | refactor(build): mode=max registry cache + main-only warmer |
#2938 | build cache |
| 6 | ci(build): isolate each PR build on its own ephemeral runner VM |
#2939 | shared-buildkit contention |
| 7 | fix(e2e): LINSTOR post-install waits on a single 15m deadline |
#2928 | LINSTOR provisioning timeouts |
| 8 | test(metallb): assert digest-pinned image form, not version
literal | #2873 | brittle metallb assertion |

## Why consolidated

These form a dependency DAG (verified from CI logs): e.g. 2943 needs
2946, 2938 needs 2874, 2928 needs 2938/2939, 2873 needs 2928. Tested
bottom-up, each lower PR runs with none of the fixes above it and can't
go green alone. This branch carries all of them, so CI runs against the
real combined state.

## Verification status

CI is green on the latest head (`5653c30`): full-suite E2E passes
end-to-end and is reproducible (2 of 3 runs on this SHA green). The one
red run was a LINSTOR tie-breaker / DRBD-metadata infra flake on the
sandbox, unrelated to the diff — a different environmental subsystem
failed each run (details in the comments below). The earlier 3-hour
crust-gather snapshot hang is fixed in `5653c30`.

## Scope beyond the original 8

The branch has grown past the 8 source PRs above with review-driven
fixes and a few production-behavior changes. For reviewer transparency,
the full set:

**Review fixes (@lexfrei review):**
- **B1 (blocker)** — `8f41910` converts the SeaweedFS
`-lock`/`-readonly` BucketClasses and the s3 service-name override into
`patches/`, wired into `make update`, so a re-vendor no longer drops
them.
- **FU1** — `cc99230` grants the cilium leak-healer `delete` on pods
(both delete remedies were RBAC-forbidden).
- **FU3** — `014de3f` adds a unit test for the HelmRelease update
conflict-retry path.
- **FU4** — `6deddc6` makes the leak-healer refuse a disconnect for any
non-terminal owner phase, not just `Running`.
- **FU7** — `f3914d2` sets `persist-credentials: false` on the
build-cache checkout (SHA-pinning is handled repo-wide in #2849).

**Production-behavior changes (not in the table above):**
- VPA `updateMode: Auto → Initial` for etcd (`6e9ff90`) and monitoring
(`0997105`), plus `vmselect`/`vmstorage` `minAllowed` floors — stops
install-time eviction churn.
- cozystack-api: `retry.RetryOnConflict` on the Application→HelmRelease
update path (`728f676`).

**E2E robustness follow-ups:** per-test crust-gather snapshots
(`50e5f94`), EtcdBackupSchedule wait (`763b85e`), tenant-node
single-deadline wait (`9e43d45`), harbor BucketClaim 10m budget
(`d7f244f`), tenant API via LoadBalancer (`9beda4e`), crust-gather pin
(`95f42aa`).

**Not addressed (by decision):** FU2 — the s3 Service keeps
`seaweedfs.name`-based naming via patch (rendered output unchanged;
cozystack renders `fullname == seaweedfs`, so it matches the
`componentName`/`fullname` siblings in practice). FU6 — fork PR build
push is handled by the existing mirror-to-same-repo-branch workflow.

## Relates to

#2946 #2944 #2943 #2874 #2938 #2939 #2928 #2873 — if this lands, those
can be closed; otherwise they remain the granular per-PR review path.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Added CI build cache warming for main branch builds.
* Added a best-effort in-cluster Cilium endpoint self-heal watchdog for
e2e installs.
* Extended SeaweedFS COSI with object-lock support plus separate
readonly access.

* **Improvements**
* Reduced e2e install flakiness with a shared readiness deadline, safer
waits, and improved cleanup/diagnostics capture.
* Prevented install-time churn by switching VPA update mode to
**Initial**.
  * Added container startup probes and updated SeaweedFS volume sizing.

* **Tests**
* Improved e2e robustness (etcd backup schedule waiting, digest-pinned
image checks, and corrected S3 port-forwarding).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
pull Bot pushed a commit to medampudi/cozystack that referenced this pull request Jun 22, 2026
…ozystack#2928)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
@myasnikovdaniil

Copy link
Copy Markdown
Contributor Author

superseded by #2948

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) backport Should change be backported on previous release kind/bug Categorizes issue or PR as related to a bug size/XL This PR changes 500-999 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant