Skip to content

Commit cefbaad

Browse files
authored
feat(platform): add default backupclass (#2716)
<!-- Thank you for making a contribution! Here are some tips for you: - Use Conventional Commits for the PR title: `type(scope): description` - Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore - Scopes are not an exhaustive list — pick the most specific scope for the change and extend the list when a genuinely new area appears. Examples: - System components: dashboard, platform, operator, cilium, kube-ovn, linstor, fluxcd, cluster-api - Managed apps: postgres, mariadb, redis, kafka, clickhouse, virtual-machine, kubernetes - Development and maintenance: api, hack, tests, ci, docs, maintenance - Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or add a `BREAKING CHANGE:` footer - If it's a work in progress, consider creating this PR as a draft. - Don't hesistate to ask for opinion and review in the community chats, even if it's still a draft. - Add the label `backport` if it's a bugfix that needs to be backported to a previous version. --> ## What this PR does align backups in charts, add CR for default strategies and backupclass. ### Screenshots <!-- REQUIRED for UI changes: attach screenshots or screen recordings demonstrating the visual impact of your changes. PRs with UI changes without screenshots will not be merged. --> ### Release note <!-- Write a release note: - Explain what has changed internally and for users. - Start with the same `type(scope):` prefix as in the PR title - Follow the guidelines at https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md. --> ```release-note ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Platform-managed default BackupClass `cozy-default`, system bucket `cozy-backups`, opt-in chart flag `useSystemBucket`, and chart behavior to skip per-release credential Secrets when enabled; Velero BackupStorageLocation support and ClickHouse/Postgres tuning for system-bucket paths. * **Behavior / Security** * Controllers project platform credentials into tenant namespaces, expose projection metrics (success/failure), skip reconciling terminal jobs, and enforce RBAC isolation for projected Secrets with clear Pending/Failed conditions on errors. * **Documentation** * Docs and READMEs updated with default-flow guidance and deprecation notes for legacy per-tenant S3 fields. * **Tests** * New unit and e2e tests validating credential projection, default flow, and strategy-specific backup/restore scenarios. <!-- review_stack_entry_start --> [![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2716?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
2 parents 8afb99b + f17ead3 commit cefbaad

69 files changed

Lines changed: 4318 additions & 223 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎api/apps/v1alpha1/clickhouse/types.go‎

Lines changed: 14 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎api/apps/v1alpha1/postgresql/types.go‎

Lines changed: 12 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎api/backups/v1alpha1/DESIGN.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,23 @@ It does **not** implement the backup logic itself.
1818

1919
This document covers only the **core** API and its contracts with drivers, not driver implementations.
2020

21+
### Platform-managed default flow
22+
23+
As of Phase 2, Cozystack also ships **opinionated defaults** for the entire stack:
24+
25+
* A platform-managed S3 bucket `cozy-backups` (provisioned through `apps.cozystack.io/Bucket` in `tenant-root`).
26+
* Pre-rendered `Strategy` CRs `cozy-default-{cnpg,mariadb,etcd,altinity,foundationdb,velero-vminstance,velero-vmdisk}`.
27+
* A single cluster-wide `BackupClass` `cozy-default` whose `spec.strategies[]` binds each supported `apps.cozystack.io/<Kind>` to the matching strategy.
28+
* A `CredentialsProjector` inside `backupstrategy-controller` that copies the bucket-controller Secret into each tenant namespace as `cozy-backups-creds` on demand (per `BackupJob`/`RestoreJob` reconcile) and into a configured list of system namespaces (e.g. `cozy-velero`) on a periodic tick.
29+
30+
Tenants reference `cozy-default` from `BackupJob`/`Plan`/`RestoreJob`; they never see the bucket controller Secret, never write to a Secret in their own namespace (default RBAC denies it), and never share an S3 path with another tenant (every default strategy template prefixes the object key with `<namespace>/<application>`).
31+
32+
The platform-managed flow is layered *on top of* — not in place of — the core API. Custom `BackupClass`/Strategy CRs targeting other buckets continue to work; tenants picking `cozy-default` simply opt into the platform's pre-wired stack.
33+
34+
**Open gap (FoundationDB):** the `cozy-default-foundationdb` Strategy CR is shipped but **not** bound by `cozy-default`. Restore for FoundationDB runs `fdbrestore` from inside the `cozy-foundationdb-operator` deployment, which does not yet mount `cozy-backups-creds` with the blob credentials file. Admins who want platform-default FDB backups today must either wire the operator deployment manually or use a custom BackupClass — see [Backup Classes](../../../docs/operations/backup-classes.md) for the follow-up plan.
35+
36+
**Tenant visibility:** projected Secrets carry no `internal.cozystack.io/tenantresource` label, so the lineage-controller webhook does not promote them to a `TenantSecret` view. Combined with the default RBAC (no core/v1.Secret verbs on tenant ServiceAccounts), the projected credentials are unreadable from a tenant kubeconfig even when they sit in the tenant's own namespace; the operator Pods mount the Secret via kubelet, which bypasses tenant RBAC.
37+
2138
---
2239

2340
## 2. Goals and non-goals

‎cmd/backupstrategy-controller/main.go‎

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
// to ensure that exec-entrypoint and run can make use of them.
2626
_ "k8s.io/client-go/plugin/pkg/client/auth"
2727

28+
corev1 "k8s.io/api/core/v1"
2829
"k8s.io/apimachinery/pkg/runtime"
2930
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
3031
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
@@ -157,6 +158,18 @@ func main() {
157158
&backupsv1alpha1.BackupClass{}: {},
158159
},
159160
},
161+
Client: client.Options{
162+
// Disable the controller-runtime cache for Secrets entirely.
163+
// The credentials projector does point Get + CreateOrUpdate
164+
// against arbitrary tenant namespaces, and a cluster-wide
165+
// Secret informer would (a) require list/watch RBAC the chart
166+
// explicitly does NOT grant, (b) keep every tenant Secret in
167+
// the controller's RAM. Direct apiserver calls keep the
168+
// blast radius bounded to the namespaces actively reconciled.
169+
Cache: &client.CacheOptions{
170+
DisableFor: []client.Object{&corev1.Secret{}},
171+
},
172+
},
160173
// LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
161174
// when the Manager ends. This requires the binary to immediately end when the
162175
// Manager is stopped, otherwise, this setting is unsafe. Setting this significantly
@@ -174,19 +187,37 @@ func main() {
174187
os.Exit(1)
175188
}
176189

190+
credentialsConfig := backupcontroller.BackupCredentialsConfig{
191+
SourceNamespace: os.Getenv("BACKUP_STORAGE_SECRET_NAMESPACE"),
192+
SourceSecretName: os.Getenv("BACKUP_STORAGE_SECRET_NAME"),
193+
TargetSecretName: os.Getenv("BACKUP_STORAGE_CREDS_SECRET_NAME"),
194+
Endpoint: os.Getenv("BACKUP_STORAGE_ENDPOINT"),
195+
Region: os.Getenv("BACKUP_STORAGE_REGION"),
196+
ForcePathStyle: os.Getenv("BACKUP_STORAGE_FORCE_PATH_STYLE"),
197+
}
198+
199+
if systemNamespaces := os.Getenv("BACKUP_STORAGE_SYSTEM_NAMESPACES"); systemNamespaces != "" {
200+
if err := mgr.Add(backupcontroller.NewSystemCredentialsProjector(mgr.GetClient(), credentialsConfig, systemNamespaces, 0)); err != nil {
201+
setupLog.Error(err, "unable to add SystemCredentialsProjector runnable")
202+
os.Exit(1)
203+
}
204+
}
205+
177206
if err = (&backupcontroller.BackupJobReconciler{
178-
Client: mgr.GetClient(),
179-
Scheme: mgr.GetScheme(),
180-
Recorder: mgr.GetEventRecorderFor("backup-controller"),
207+
Client: mgr.GetClient(),
208+
Scheme: mgr.GetScheme(),
209+
Recorder: mgr.GetEventRecorderFor("backup-controller"),
210+
CredentialsConfig: credentialsConfig,
181211
}).SetupWithManager(mgr); err != nil {
182212
setupLog.Error(err, "unable to create controller", "controller", "BackupJob")
183213
os.Exit(1)
184214
}
185215

186216
if err = (&backupcontroller.RestoreJobReconciler{
187-
Client: mgr.GetClient(),
188-
Scheme: mgr.GetScheme(),
189-
Recorder: mgr.GetEventRecorderFor("restore-controller"),
217+
Client: mgr.GetClient(),
218+
Scheme: mgr.GetScheme(),
219+
Recorder: mgr.GetEventRecorderFor("restore-controller"),
220+
CredentialsConfig: credentialsConfig,
190221
}).SetupWithManager(mgr); err != nil {
191222
setupLog.Error(err, "unable to create controller", "controller", "RestoreJob")
192223
os.Exit(1)

0 commit comments

Comments
 (0)