Skip to content

E2e/dataexport recovery - #93

Merged
kneumoin merged 3 commits into
next-phasefrom
e2e/dataexport-recovery
Aug 3, 2026
Merged

E2e/dataexport recovery#93
kneumoin merged 3 commits into
next-phasefrom
e2e/dataexport-recovery

Conversation

@kneumoin

@kneumoin kneumoin commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Description

Adds an e2e suite for DataExport recovery against storage-foundation volume borrow/return behavior.

Covered paths:

  • export claim loss and exporter Deployment recreation without a second borrow
  • refuse return while a pod still holds the claim; survive controller restart; finish after the pod is gone
  • foreign claim occupying the export claim name (left untouched)
  • live VolumeAttachment blocking return
  • namesake replacement of the export claim (report, return volume, do not touch the namesake)
  • DataExport deleted mid-recovery (barrier keeps the object until the volume is home)
  • volume already borrowed by another export (refuse start, leave volume as found)
  • legacy export without recorded identity (refuse to guess; operator restores identity so return can finish)

Also adds per-scenario failure diagnostics (DataExport status, claims, volume, exporter Deployment, pods, VolumeAttachment, object-derived checkpoint, filtered controller logs) and moves the suite hold finalizer outside deckhouse.io so deny-deckhouse-finalizers.deckhouse.io does not pin cleanup.

No production controller or cluster-component changes; test-only.

Why do we need it, and what problem does it solve?

DataExport recovery depends on correct interaction with storage-foundation (borrow/return, claim identity, attachment barriers). Without automated coverage, regressions in these paths are easy to miss and hard to diagnose on failure.

This suite locks the recovery contract and gives actionable diagnostics when a scenario fails.

What is the expected result?

After applying:

  • the new DataExport recovery e2e suite is present and runnable with the existing state-snapshotter e2e harness
  • recovery scenarios above pass against a cluster with storage-foundation + state-snapshotter
  • on failure, the suite dumps the per-scenario diagnostic report instead of a bare assert
  • MUST NOT change runtime module behavior, CRDs, RBAC, or Helm templates (e2e-only diff)

Checklist

  • The code is covered by unit tests.
  • e2e tests passed.
  • Documentation updated according to the changes.
  • Changes were tested in the Kubernetes cluster manually.

…e borrow/return

Add an e2e suite for DataExport recovery: claim loss and recreation, foreign or
namesake claims, live VolumeAttachment blocking return, deletion mid-recovery,
already-borrowed volumes, and legacy exports without recorded identity. Include
per-scenario failure diagnostics and move the suite hold finalizer outside
deckhouse.io so admission does not pin cleanup.

Signed-off-by: Neumoin, Konstantin <konstantin.neumoin@flant.com>
go-lint.sh installed golangci-lint v1.64.5 while CI installs v2.9.0, so a local run and the
check that gates the merge could not report the same thing: the configuration file of one major
version is rejected outright by the other, and CI was working only because its action migrates
the config on the fly. Committing the migrated config makes that step a no-op and gives both the
same file. The migration is the output of the same v2.9.0, and it drops what v2 no longer has:
gosimple (merged into staticcheck) and typecheck (never a linter).

Dropping the `go: "1.25"` pin along with it. It existed to get past the version check when the
binary was built with an older Go than the modules target, which is now handled where the binary
is built — the toolchain is derived from the newest go directive among the modules instead of
being hardcoded, so it cannot fall behind them. Verified that a linter built with 1.26.4, the
version CI uses, lints modules targeting 1.26.5 without the pin; keeping it would only ask the
linter to analyse 1.26 code as if it were 1.25.

Signed-off-by: Neumoin, Konstantin <konstantin.neumoin@flant.com>
@kneumoin kneumoin self-assigned this Aug 3, 2026
Enabling the module stack rewrites each ModulePullOverride, and an unset
<MODULE>_MODULE_PULL_OVERRIDE resolves to the literal "main" from cluster_config.yml. A run that
forgets the variable therefore does not test the build under review, it replaces it — and not only
the image: Deckhouse re-downloads the module and re-runs ModuleEnsureCRDs from the new copy, so the
CRDs revert too. The specs then assert against a schema that prunes the status fields the controller
writes, which the API server reports as a warning rather than an error, and the failure surfaces as
an empty field far from its cause. That is exactly how a recovery run failed: status.recovery had
been dropped from the schema one minute before the assertion read it.

BeforeSuite now compares the live ModulePullOverride with the tag the run is about to apply and
stops, naming the module, both tags and the variable to set; E2E_ALLOW_MODULE_REPIN=true re-pins
deliberately. The check is "do not move an existing pin" rather than "always require the variable",
because a run against main is legitimate and a module the cluster does not pin yet has nothing to
lose, so a fresh cluster behaves as before.

Signed-off-by: Neumoin, Konstantin <konstantin.neumoin@flant.com>
@kneumoin
kneumoin merged commit 7dc23c4 into next-phase Aug 3, 2026
11 of 13 checks passed
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.

1 participant