Skip to content

feat(backups): make default restore drivers handle spec.options uniformly and predictably #3549

Open

Description

Problem / motivation

RestoreJob.spec.options is a single shared, driver-opaque runtime.RawExtension (api/backups/v1alpha1/restorejob_types.go), and each strategy driver in internal/backupcontroller/ decodes it into its own typed struct. The CRD contract is uniform, but the behavior of the default drivers around that field is not — which is confusing and unpredictable for tenants who expect the same field to behave the same way regardless of the database.

Current state on main:

Driver Options it reads Malformed spec.options
CNPG (Postgres) recoveryTime, restoreTimeoutSeconds, walArchiveTimeoutSeconds permissive — log event + fall back to defaults (parseCNPGRestoreOptions, cnpgstrategy_controller.go:718)
MariaDB restoreTimeoutSeconds permissive — log event + defaults (mariadbstrategy_controller.go:501)
etcd restoreTimeoutSeconds strict — terminal Failed (etcdstrategy_controller.go:662)
FoundationDB restoreTimeoutSeconds strict — terminal Failed (foundationdbstrategy_controller.go:860)
Altinity (ClickHouse) none — spec.options is never read option is silently ignored entirely

Two concrete predictability problems fall out of this:

  1. Malformed-options handling is inconsistent. The same bad JSON in spec.options fails the restore on etcd/FoundationDB but is silently swallowed (restore proceeds with defaults) on CNPG/MariaDB. A tenant cannot predict whether a typo aborts or is ignored.

  2. Unsupported options are silently dropped. Altinity does not read spec.options at all, so a tenant who sets restoreTimeoutSeconds (supported everywhere else) or recoveryTime (expecting PITR) on a ClickHouse RestoreJob gets a silent no-op — no error, no event, no condition. The restore just quietly ignores their intent. The common restoreTimeoutSeconds knob is honored by 4 of 5 default drivers but not ClickHouse.

Proposed solution

Give the default drivers one uniform, predictable contract for spec.options:

  1. One shared malformed-options policy. Pick a single behavior for all default drivers and apply it via a shared helper. Recommendation: fail fast — a malformed spec.options becomes a terminal Failed with a clear message + Warning event (the etcd/FDB behavior), because silently restoring with defaults after ignoring a tenant's explicit override is the more surprising outcome. (If permissive is preferred instead, make it uniform — the point is one policy, not two.)

  2. A shared CommonRestoreOptions honored by every driver. The Velero driver already models this pattern (velerostrategy_controller.go:92). Extract a common struct (at minimum restoreTimeoutSeconds) embedded by every strategy so the common knobs are defined and parsed once and work identically on ClickHouse too.

  3. Surface unsupported/ignored options instead of dropping them. When a tenant sets an option a given driver does not implement (e.g. recoveryTime / PITR on ClickHouse or MariaDB, which have no point-in-time recovery), emit a Warning event / set a condition — "recoveryTime (PITR) is not supported for ClickHouse and was ignored" — rather than silently no-op'ing. Predictable > silent.

Net effect: same field, same validation, same failure mode across all default drivers; capability differences (e.g. only CNPG supports PITR) are reported explicitly instead of discovered by surprise.

Scope check

  • This affects a single component / package (internal/backupcontroller/ + the shared api/backups/v1alpha1 options contract)
  • I can describe the expected behavior concretely

Alternatives considered

  • Leave as-is / document the differences. Rejected: the whole selling point of the shared backups.cozystack.io CRD is a uniform API; per-driver quirks in the same field undercut that and surface as tenant support tickets.
  • Per-driver typed option subschemas on the CRD instead of one opaque blob. Heavier change to the API surface and arguably a design-proposal; this issue keeps the opaque-blob model and only unifies how drivers parse/validate/report it.

Additional context

  • Shared spec: api/backups/v1alpha1/restorejob_types.go (RestoreJobSpec.Options *runtime.RawExtension, +kubebuilder:pruning:PreserveUnknownFields).
  • Option structs: CNPGRestoreOptions (cnpgstrategy_controller.go:1721), MariaDBRestoreOptions (mariadbstrategy_controller.go:849), EtcdRestoreOptions (etcdstrategy_controller.go:1406), FoundationDBRestoreOptions (foundationdbstrategy_controller.go:1240), Velero CommonRestoreOptions/RestoreOptions (velerostrategy_controller.go:92).
  • Altinity driver (altinitystrategy_controller.go) contains no reference to restoreJob.Spec.Options.

Activity

  1. added
    area/databaseIssues or PRs related to managed databases (postgres, mariadb, redis, etcd, kafka, clickhouse)
    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
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/databaseIssues or PRs related to managed databases (postgres, mariadb, redis, etcd, kafka, clickhouse)kind/featureCategorizes issue or PR as related to a new featurepriority/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