Skip to main content

Crate kopiur_api

Crate kopiur_api 

Source
Expand description

§kopiur-api

Strongly-typed CRD definitions and shared, controller-free logic for Kopiur, the Kopia-native Kubernetes backup operator (ADR-0003).

§Role in the workspace

This crate holds the 7 CRD types in API group kopiur.home-operations.com, version v1alpha1Repository (ns), ClusterRepository (cluster), BackupConfig, Backup, BackupSchedule, Restore, and Maintenance — together with the shared pure logic every consumer needs: validation (validate), identity resolution (resolve_identity), schedule jitter (jitter), and GFS retention (select_kept).

It deliberately has no controller-runtime dependencies — no kube::Client, no tokio. Downstream tools (a custom backup-triggering controller, a CI linter for BackupConfig manifests, a dashboard) can depend on the API types and shared logic alone, without pulling in the async runtime or the cluster client (ADR §5.1). The webhook and the controller both import the same validate/ identity/retention functions, so validation and resolution behave identically across call sites (“one validator, two callers”).

§The load-bearing idea: type-safety end-to-end (ADR §5.5)

Every discriminated union in the CRD surface is a Rust enum: Backend, AllowedNamespaces, DeletionPolicy, RestoreSource, Hook, and friends.

A deserialized value is always exactly one variant — an invalid “two backends at once” or “no backend” state is unrepresentable — and reconcilers match exhaustively. A new variant added later cannot compile until every handler accounts for it. For backup software, where a silently-unhandled case can lose user data, this eliminates the highest-severity class of “controller silently dropped data” bugs. This is the whole reason Kopiur is Rust and not Go; preserve this property in every change (prefer an enum + exhaustive match over if let / _ => catch-alls).

§Key types

TypePurpose
Repository / ClusterRepositoryThe kopia repository as a first-class resource (namespaced / cluster-scoped).
BackendThe storage backend union (s3, azure, gcs, b2, filesystem, sftp, webDav, rclone).
BackupConfigThe backup recipe (sources, retention, hooks).
BackupA single backup invocation, owning its snapshot via finalizer.
BackupScheduleThe schedule that emits Backups on a cron.
RestoreA restore request (RestoreSource / RestoreTarget).
Maintenancekopia maintenance as a first-class, default-managed concern.
DeletionPolicyDelete / Retain / Orphan — ties snapshot lifecycle to the CR.

Shared pure logic (no controller deps):

FunctionPurpose
resolve_identityRender the kopia username@hostname:path identity, pinned to status at admission.
select_keptGFS retention: decide which backups to keep.
jitter_offset / substitute_hDeterministic H/jitter from (scheduleUID, slot).

§Conventions

Before editing this crate, read docs/dev/api-conventions.md. The load-bearing rules:

  • Discriminated unions are externally-tagged enums (backend: { s3: {...} }), not #[serde(tag = "...")] — internally-tagged enums break Kubernetes structural-schema generation.
  • No Eq on structs that embed k8s-openapi types (LabelSelector, ResourceRequirements, SecurityContext, …) — they are PartialEq only.
  • Sub-objects, not leaf fields, for every credential/policy/identity/schedule surface, so future fields slot in without API breakage.

§Usage

Construct or deserialize a Backend the way the API server does (JSON value → typed) and match it exhaustively:

use kopiur_api::Backend;

// External tagging: the variant key selects exactly one backend.
let backend: Backend = serde_json::from_value(serde_json::json!({
    "s3": { "bucket": "my-backups", "region": "us-east-1" }
}))
.unwrap();

// A deserialized Backend is always exactly one variant -> exhaustive match.
let summary = match &backend {
    Backend::S3(s3) => format!("s3://{}", s3.bucket),
    Backend::Azure(_) => "azure".into(),
    Backend::Gcs(_) => "gcs".into(),
    Backend::B2(_) => "b2".into(),
    Backend::Filesystem(_) => "filesystem".into(),
    Backend::Sftp(_) => "sftp".into(),
    Backend::WebDav(_) => "webdav".into(),
    Backend::Rclone(_) => "rclone".into(),
};
assert_eq!(summary, "s3://my-backups");

// The stable discriminant is independent of the camelCase wire key.
assert_eq!(backend.kind_str(), "S3");

Note: deserialize via serde_json (the API-server path), never serde_yaml directly into a typed value — serde_yaml 0.9 mis-encodes externally-tagged enums. For YAML tests, go YAML → serde_json::Value → typed.

§See also

Re-exports§

pub use backend::Backend;
pub use backup::Backup;
pub use backup::BackupPhase;
pub use backup::BackupSpec;
pub use backup::BackupStats;
pub use backup::BackupStatus;
pub use backup::BackupTiming;
pub use backup::Origin;
pub use backup_config::BackupConfig;
pub use backup_config::BackupConfigSpec;
pub use backup_config::BackupConfigStatus;
pub use backup_config::CopyMethod;
pub use backup_config::GroupBy;
pub use backup_config::Hook;
pub use backup_config::SourcePathStrategy;
pub use backup_schedule::BackupSchedule;
pub use backup_schedule::BackupScheduleSpec;
pub use backup_schedule::BackupScheduleStatus;
pub use backup_schedule::ConcurrencyPolicy;
pub use backup_schedule::ScheduleSpec;
pub use cluster_repository::AllowedNamespaces;
pub use cluster_repository::ClusterRepository;
pub use cluster_repository::ClusterRepositorySpec;
pub use cluster_repository::ClusterRepositoryStatus;
pub use cluster_repository::IdentityTemplate;
pub use common::ConfigRef;
pub use common::CronSpec;
pub use common::DeletionPolicy;
pub use common::ObjectRef;
pub use common::PhaseLabel;
pub use maintenance::Maintenance;
pub use maintenance::MaintenanceSchedule;
pub use maintenance::MaintenanceSpec;
pub use maintenance::MaintenanceStatus;
pub use maintenance::Ownership;
pub use maintenance::RepositoryMaintenanceSpec;
pub use maintenance::TakeoverPolicy;
pub use maintenance::default_maintenance_schedule;
pub use repository::Repository;
pub use repository::RepositoryPhase;
pub use repository::RepositorySpec;
pub use repository::RepositoryStatus;
pub use restore::OnMissingSnapshot;
pub use restore::Restore;
pub use restore::RestorePhase;
pub use restore::RestoreSource;
pub use restore::RestoreSpec;
pub use restore::RestoreStatus;
pub use restore::RestoreTarget;
pub use error::ValidationError;
pub use error::ValidationResult;
pub use identity::IdentityInputs;
pub use identity::identity_string;
pub use identity::resolve_identity;
pub use jitter::offset as jitter_offset;
pub use jitter::substitute_h;
pub use retention::BackupLike;
pub use retention::KeptSet;
pub use retention::select_kept;

Modules§

backend
Storage backends for a kopia repository.
backup
The Backup CRD — a single kopia snapshot as a Kubernetes object. ADR-0001 §3.4, ADR-0003 §4.5.
backup_config
The BackupConfig CRD — the recipe. Idempotent; runs nothing on its own. ADR-0001 §3.3, ADR-0003 §4.8.
backup_schedule
The BackupSchedule CRD — when a backup runs. Creates Backup CRs on a cron schedule in the BackupConfig’s namespace. ADR-0001 §3.5, ADR-0003 §4.4.
cluster_repository
The ClusterRepository CRD — a cluster-scoped, shared kopia repository operated by a platform team. ADR-0001 §3.2, ADR-0003 §3.2.
common
Shared sub-objects reused across multiple CRDs.
error
Typed validation errors shared by the admission webhook and the controller.
identity
Kopia identity resolution (ADR §4.2).
jitter
Deterministic schedule jitter and Jenkins-style H substitution (ADR §4.1).
maintenance
The Maintenance CRD — schedules kopia maintenance run quick + full and manages the ownership lease. At most one per repository. ADR-0001 §3.7.
repository
The Repository CRD — a namespaced kopia repository. ADR-0003 §3.1.
restore
The Restore CRD — a restore from a snapshot/identity to a PVC, or a passive populator source. ADR-0001 §3.6, ADR-0003 §4.6.
retention
Grandfather-father-son (GFS) retention selection (ADR §4.4).
validate
Cross-field validation the type system can’t express (ADR §2.2 principle 8).

Constants§

GROUP
The CRD API group for all kopiur resources.
VERSION
The current (and only, per ADR §8) API version.