## What this PR does This patch creates a new backups.cozystack.io API group which includes backup-related resources owned exclusively by Cozystack core. A cronjob-like controller is implemented to create backup jobs that will be handled by appropriate third-party or external controllers. ### Release note ```release-note [backups] Implement the core backup API and an accompanying controller. ``` Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
12 KiB
Cozystack Backups – Core API & Contracts (Draft)
1. Overview
Cozystack’s backup subsystem provides a generic, composable way to back up and restore managed applications:
- Every application instance can have one or more backup plans.
- Backups are stored in configurable storage locations.
- The mechanics of how a backup/restore is performed are delegated to strategy drivers, each implementing driver-specific BackupStrategy CRDs.
The core API:
- Orchestrates when backups happen and where they’re stored.
- Tracks what backups exist and their status.
- Defines contracts with drivers via shared resources (
BackupJob,Backup,RestoreJob).
It does not implement the backup logic itself.
This document covers only the core API and its contracts with drivers, not driver implementations.
2. Goals and non-goals
Goals
-
Provide a stable core API for:
- Declaring backup plans per application.
- Configuring storage targets (S3, in-cluster bucket, etc.).
- Tracking backup artifacts.
- Initiating and tracking restores.
-
Allow multiple strategy drivers to plug in, each supporting specific kinds of applications and strategies.
-
Let application/product authors implement backup for their kinds by:
- Creating Plan objects referencing a driver-specific strategy.
- Not having to write a backup engine themselves.
Non-goals
- Implement backup logic for any specific application or storage backend.
- Define the internal structure of driver-specific strategy CRDs.
- Handle tenant-facing UI/UX (that’s built on top of these APIs).
3. Architecture
High-level components:
-
Core backups controller(s) (Cozystack-owned):
-
Group:
backups.cozystack.io -
Own:
PlanBackupJobBackupRestoreJob
-
Responsibilities:
-
Schedule backups based on
Plan. -
Create
BackupJobobjects when due. -
Provide stable contracts for drivers to:
- Perform backups and create
Backups. - Perform restores based on
Backups.
- Perform backups and create
-
-
-
Strategy drivers (pluggable, possibly third-party):
-
Their own API groups, e.g.
jobdriver.backups.cozystack.io. -
Own strategy CRDs (e.g.
JobBackupStrategy). -
Implement controllers that:
- Watch
BackupJob/RestoreJob. - Match runs whose
strategyRefGVK they support. - Execute backup/restore logic.
- Create and update
Backupand run statuses.
- Watch
-
Strategy drivers and core communicate entirely via Kubernetes objects; there are no webhook/HTTP calls between them.
-
Storage drivers (pluggable, possibly third-party):
- TBD
4. Core API resources
4.1 Plan
Group/Kind
backups.cozystack.io/v1alpha1, Kind=Plan
Purpose Describe when, how, and where to back up a specific managed application.
Key fields (spec)
type PlanSpec struct {
// Application to back up.
ApplicationRef corev1.TypedLocalObjectReference `json:"applicationRef"`
// Where backups should be stored.
StorageRef corev1.TypedLocalObjectReference `json:"storageRef"`
// Driver-specific BackupStrategy to use.
StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"`
// When backups should run.
Schedule PlanSchedule `json:"schedule"`
}
PlanSchedule (initially) supports only cron:
type PlanScheduleType string
const (
PlanScheduleTypeEmpty PlanScheduleType = ""
PlanScheduleTypeCron PlanScheduleType = "cron"
)
type PlanSchedule struct {
// Type is the schedule type. Currently only "cron" is supported.
// Defaults to "cron".
Type PlanScheduleType `json:"type,omitempty"`
// Cron expression (required for cron type).
Cron string `json:"cron,omitempty"`
}
Plan reconciliation contract
Core Plan controller:
-
Read schedule from
spec.scheduleand compute the next fire time. -
When due:
-
Create a
BackupJobin the same namespace:spec.planRef.name = plan.Namespec.applicationRef = plan.spec.applicationRefspec.storageRef = plan.spec.storageRefspec.strategyRef = plan.spec.strategyRefspec.triggeredBy = "Plan"
-
Set
ownerReferencesso theBackupJobis owned by thePlan.
-
The Plan controller does not:
- Execute backups itself.
- Modify driver resources or
Backupobjects. - Touch
BackupJob.specafter creation.
4.2 Storage
API Shape
TBD
Storage usage
PlanandBackupJobreferenceStorageviaTypedLocalObjectReference.- Drivers read
Storageto know how/where to store or read artifacts. - Core treats
Storagespec as opaque; it does not directly talk to S3 or buckets.
4.3 BackupJob
Group/Kind
backups.cozystack.io/v1alpha1, Kind=BackupJob
Purpose
Represent a single execution of a backup operation, typically created when a Plan fires or when a user triggers an ad-hoc backup.
Key fields (spec)
type BackupJobSpec struct {
// Plan that triggered this run, if any.
PlanRef *corev1.LocalObjectReference `json:"planRef,omitempty"`
// Application to back up.
ApplicationRef corev1.TypedLocalObjectReference `json:"applicationRef"`
// Storage to use.
StorageRef corev1.TypedLocalObjectReference `json:"storageRef"`
// Driver-specific BackupStrategy to use.
StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"`
// Informational: what triggered this run ("Plan", "Manual", etc.).
TriggeredBy string `json:"triggeredBy,omitempty"`
}
Key fields (status)
type BackupJobStatus struct {
Phase BackupJobPhase `json:"phase,omitempty"`
BackupRef *corev1.LocalObjectReference `json:"backupRef,omitempty"`
StartedAt *metav1.Time `json:"startedAt,omitempty"`
CompletedAt *metav1.Time `json:"completedAt,omitempty"`
Message string `json:"message,omitempty"`
Conditions []metav1.Condition `json:"conditions,omitempty"`
}
BackupJobPhase is one of: Pending, Running, Succeeded, Failed.
BackupJob contract with drivers
-
Core creates
BackupJoband must treatspecas immutable afterwards. -
Each driver controller:
- Watches
BackupJob. - Reconciles runs where
spec.strategyRef.apiGroup/kindmatches its strategy type(s).
- Watches
-
Driver responsibilities:
-
On first reconcile:
- Set
status.startedAtif unset. - Set
status.phase = Running.
- Set
-
Resolve inputs:
- Read
Strategy(driver-owned CRD),Storage,Application, optionallyPlan.
- Read
-
Execute backup logic (implementation-specific).
-
On success:
- Create a
Backupresource (see below). - Set
status.backupRefto the createdBackup. - Set
status.completedAt. - Set
status.phase = Succeeded.
- Create a
-
On failure:
- Set
status.completedAt. - Set
status.phase = Failed. - Set
status.messageand conditions.
- Set
-
Drivers must not modify BackupJob.spec or delete BackupJob themselves.
4.4 Backup
Group/Kind
backups.cozystack.io/v1alpha1, Kind=Backup
Purpose Represent a single backup artifact for a given application, decoupled from a particular run. usable as a stable, listable “thing you can restore from”.
Key fields (spec)
type BackupSpec struct {
ApplicationRef corev1.TypedLocalObjectReference `json:"applicationRef"`
PlanRef *corev1.LocalObjectReference `json:"planRef,omitempty"`
StorageRef corev1.TypedLocalObjectReference `json:"storageRef"`
StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"`
TakenAt metav1.Time `json:"takenAt"`
DriverMetadata map[string]string `json:"driverMetadata,omitempty"`
}
Key fields (status)
type BackupStatus struct {
Phase BackupPhase `json:"phase,omitempty"` // Pending, Ready, Failed, etc.
Artifact *BackupArtifact `json:"artifact,omitempty"`
Conditions []metav1.Condition `json:"conditions,omitempty"`
}
BackupArtifact describes the artifact (URI, size, checksum).
Backup contract with drivers
-
On successful completion of a
BackupJob, the driver:-
Creates a
Backupin the same namespace (typically owned by theBackupJob). -
Populates
specfields with:- The application, storage, strategy references.
takenAt.- Optional
driverMetadata.
-
Sets
statuswith:phase = Ready(or equivalent when fully usable).artifactdescribing the stored object.
-
-
Core:
-
Treats
Backupspec as mostly immutable and opaque. -
Uses it to:
- List backups for a given application/plan.
- Anchor
RestoreJoboperations. - Implement higher-level policies (retention) if needed.
-
4.5 RestoreJob
Group/Kind
backups.cozystack.io/v1alpha1, Kind=RestoreJob
Purpose
Represent a single restore operation from a Backup, either back into the same application or into a new target application.
Key fields (spec)
type RestoreJobSpec struct {
// Backup to restore from.
BackupRef corev1.LocalObjectReference `json:"backupRef"`
// Target application; if omitted, drivers SHOULD restore into
// backup.spec.applicationRef.
TargetApplicationRef *corev1.TypedLocalObjectReference `json:"targetApplicationRef,omitempty"`
}
Key fields (status)
type RestoreJobStatus struct {
Phase RestoreJobPhase `json:"phase,omitempty"` // Pending, Running, Succeeded, Failed
StartedAt *metav1.Time `json:"startedAt,omitempty"`
CompletedAt *metav1.Time `json:"completedAt,omitempty"`
Message string `json:"message,omitempty"`
Conditions []metav1.Condition `json:"conditions,omitempty"`
}
RestoreJob contract with drivers
-
RestoreJob is created either manually or by core.
-
Driver controller:
-
Watches
RestoreJob. -
On reconcile:
-
Fetches the referenced
Backup. -
Determines effective:
- Strategy:
backup.spec.strategyRef. - Storage:
backup.spec.storageRef. - Target application:
spec.targetApplicationReforbackup.spec.applicationRef.
- Strategy:
-
If effective strategy’s GVK is one of its supported strategy types → driver is responsible.
-
-
Behaviour:
-
On first reconcile, set
status.startedAtandphase = Running. -
Resolve
Backup,Storage,Strategy, target application. -
Execute restore logic (implementation-specific).
-
On success:
- Set
status.completedAt. - Set
status.phase = Succeeded.
- Set
-
On failure:
- Set
status.completedAt. - Set
status.phase = Failed. - Set
status.messageand conditions.
- Set
-
-
Drivers must not modify RestoreJob.spec or delete RestoreJob.
5. Strategy drivers (high-level)
Strategy drivers are separate controllers that:
-
Define their own strategy CRDs (e.g.
JobBackupStrategy) in their own API groups:- e.g.
jobdriver.backups.cozystack.io/v1alpha1, Kind=JobBackupStrategy
- e.g.
-
Implement the BackupJob contract:
- Watch
BackupJob. - Filter by
spec.strategyRef.apiGroup/kind. - Execute backup logic.
- Create/update
Backup.
- Watch
-
Implement the RestoreJob contract:
- Watch
RestoreJob. - Resolve
Backup, then effectivestrategyRef. - Filter by effective strategy GVK.
- Execute restore logic.
- Watch
The core backups API does not dictate:
- The fields and structure of driver strategy specs.
- How drivers implement backup/restore internally (Jobs, snapshots, native operator CRDs, etc.).
Drivers are interchangeable as long as they respect:
- The
BackupJobandRestoreJobcontracts. - The shapes and semantics of
Backupobjects.
6. Summary
The Cozystack backups core API:
-
Uses a single group,
backups.cozystack.io, for all core CRDs. -
Cleanly separates:
- When & where (Plan + Storage) – core-owned.
- What backup artifacts exist (Backup) – driver-created but cluster-visible.
- Execution lifecycle (BackupJob, RestoreJob) – shared contract boundary.
-
Allows multiple strategy drivers to implement backup/restore logic without entangling their implementation with the core API.