Expand description
Typed validation errors shared by the admission webhook and the controller.
Per ADR-0003 §2.2 (principle 8) and the SKILL “one validator, two callers”
rule, cross-field validation lives in crate::validate as pure functions
returning these typed errors. The webhook rejects at admission; the controller
calls the same functions defensively before reconcile. The error type is the
contract between them, so messages must be actionable — they end up in a
kubectl apply rejection and in controller logs verbatim.
§Accumulation vs. fail-fast
Per-field helpers (e.g. crate::validate::validate_repository_ref) are
fail-fast: they return the first problem they find as ValidationResult.
The per-CRD aggregate validators (validate_backup_config, …) accumulate
every independent problem into a Vec<ValidationError> so a user fixing one
manifest sees all issues at once rather than playing whack-a-mole across
re-applies. Both styles share this one error enum.
use kopiur_api::ValidationError;
// Messages are written for a human reading a rejected `kubectl apply` — they
// say what is wrong and why, embedding the offending value.
let err = ValidationError::DiscoveredMustRetain { got: "Delete".to_string() };
assert!(err.to_string().contains("origin: discovered"));
assert!(err.to_string().contains("Delete"));
// `ValidationResult` defaults its Ok type to `()` for the pass/fail case.
let ok: kopiur_api::ValidationResult = Ok(());
assert!(ok.is_ok());Enums§
- Validation
Error - A single cross-field validation failure.
PartialEqso tests can assert the exact variant; messages are written for an end user reading a rejected apply.
Type Aliases§
- Validation
Result - Result alias for validators. Defaults to
()for the common “pass/fail with no value” case.