Skip to content

feat(api): add RecoverySpec and backup compression fields to CRD - #12

Merged
birdmanmandbir merged 10 commits into
mainfrom
feat/backup-recovery-crd-types
Jan 3, 2026
Merged

feat(api): add RecoverySpec and backup compression fields to CRD#12
birdmanmandbir merged 10 commits into
mainfrom
feat/backup-recovery-crd-types

Conversation

@birdmanmandbir

Copy link
Copy Markdown
Contributor

Summary

  • Add RecoverySpec for point-in-time recovery (PITR) from existing backups
  • Add compression fields (walCompression, dataCompression) to BackupSpec
  • Add ConditionTypeBackupReady status condition
  • Add CEL validation preventing backup and recovery from being enabled simultaneously
  • Add plugin-barman-cloud dependency for ObjectStore types
  • Upgrade CNPG to v1.28.0

New CRD Fields

spec:
  database:
    backup:
      walCompression: gzip    # gzip, bzip2, snappy, none
      dataCompression: gzip   # gzip, bzip2, snappy, none
    recovery:
      enabled: true
      serverName: "original-cluster"
      targetTime: "2026-01-01T00:00:00Z"
      destinationPath: "s3://bucket/path/"
      endpointURL: "https://..."
      s3CredentialsSecret: "my-s3-creds"

Validation Rules

  • Backup and recovery cannot both be enabled
  • Recovery requires: serverName, destinationPath, s3CredentialsSecret

Part of #7

- Add RecoverySpec for point-in-time recovery (PITR) support
  - serverName, targetTime, destinationPath, endpointURL, s3CredentialsSecret
- Add compression fields to BackupSpec (walCompression, dataCompression)
  - Supports gzip, bzip2, snappy, none
- Add ConditionTypeBackupReady status condition
- Add CEL validation: backup and recovery cannot both be enabled
- Add plugin-barman-cloud dependency for ObjectStore types
- Register barmancloudv1 scheme in main.go
- Upgrade CNPG to v1.28.0

Part of #7
…r reconciliation

- Add objectstore.go with BuildObjectStore() and BuildRecoveryObjectStore()
- Add scheduled_backup.go with BuildScheduledBackup()
- Extend cluster.go with buildBootstrapConfiguration() for recovery bootstrap
- Add external clusters configuration for PITR recovery
- Add reconcileBackupInfrastructure() to controller
- Add RBAC permissions for objectstores and scheduledbackups
- Register barmancloudv1 types in SetupWithManager

Part of #7
@birdmanmandbir

Copy link
Copy Markdown
Contributor Author

PR Review Summary

Critical Issues (2 found)

Agent Issue Location
silent-failure-hunter Silent nil ObjectStore when backup enabled - if BuildObjectStore returns nil unexpectedly, backups fail silently controller.go:706-730
silent-failure-hunter Silent nil recovery ObjectStore - recovery will fail at CNPG level with confusing errors controller.go:762-788

Important Issues (4 found)

Agent Issue Location
code-reviewer Missing status update after setting BackupReady condition - condition may be lost on later errors controller.go:759
code-reviewer Recovery bootstrap missing PostInitApplicationSQLRefs - PITR may leave cluster without Supabase schema cluster.go:155-175
code-reviewer Potential race condition - ObjectStore created AFTER Cluster references it controller.go:133-136
silent-failure-hunter Missing status condition updates on backup failures - users can't see failure via kubectl describe controller.go:700-792

Suggestions (5 found)

Agent Suggestion Location
code-reviewer Use guard statements to reduce cyclomatic complexity controller.go:705-730
type-design-analyzer Add pattern validation for TargetTime (RFC 3339) supabaseproject_types.go
type-design-analyzer Add pattern validation for DestinationPath (s3://) supabaseproject_types.go
type-design-analyzer Extract shared S3Config type to reduce duplication supabaseproject_types.go
type-design-analyzer Define CompressionType alias for type safety supabaseproject_types.go

Strengths

  • Good use of CEL validation rules in CRD types
  • Proper owner references for garbage collection
  • Consistent use of pure builder functions
  • Excellent mutual exclusion constraint (backup/recovery) via CEL
  • Error wrapping with context preserves error chain
  • RBAC rules updated correctly

Recommended Action

  1. Fix critical issues first - The silent nil checks should explicitly error when backup/recovery is enabled
  2. Address ordering - Move ObjectStore creation BEFORE Cluster creation
  3. Add condition updates on error paths - Users need visibility into failures
  4. Consider the PostInitApplicationSQLRefs gap for recovery mode

- Move backup infrastructure reconciliation before cluster creation
  (Phase 2.5 instead of 3.5) to avoid race condition where Cluster
  references ObjectStore before it exists
- Remove silent nil checks - now explicitly error when builders
  return nil unexpectedly when backup/recovery is enabled
- Add status updates after setting BackupReady condition on all
  error paths for user visibility
- Extract createOrUpdateObjectStore and createOrUpdateScheduledBackup
  helper functions to reduce code duplication
@birdmanmandbir

Copy link
Copy Markdown
Contributor Author

Fix Status Update

All critical and important issues have been addressed in commit a72dcf5:

Critical Issues ✅

Issue Status
Silent nil ObjectStore when backup enabled ✅ Fixed - now explicitly errors
Silent nil recovery ObjectStore ✅ Fixed - now explicitly errors

Important Issues ✅

Issue Status
Missing status update after BackupReady condition ✅ Fixed - added Status().Update() on all error paths
Potential race condition (ObjectStore after Cluster) ✅ Fixed - moved to Phase 2.5 (before cluster creation)
Missing status condition updates on backup failures ✅ Fixed - BackupReady=False set on all error paths
Recovery bootstrap missing PostInitApplicationSQLRefs ⚪ Not a bug - recovery restores from backup which already contains schema

Suggestions (deferred)

  • Pattern validation for TargetTime/DestinationPath
  • Extract shared S3Config type
  • Define CompressionType alias

- Define CompressionType alias for type safety with enum constants
- Extract S3Config embedded type to reduce duplication between
  BackupSpec and RecoverySpec
- Add RFC 3339 pattern validation for TargetTime field
- Add S3 path pattern validation for DestinationPath field
- Refactor reconcileBackupInfrastructure using guard statements:
  split into reconcileBackup, reconcileRecovery, and failBackup helper
@birdmanmandbir

Copy link
Copy Markdown
Contributor Author

Suggestions Status Update

All 5 suggestions from the PR review have been implemented in commit 13f94eb:

Suggestion Status Implementation
Use guard statements to reduce cyclomatic complexity ✅ Done Split into reconcileBackup(), reconcileRecovery(), failBackup()
Add pattern validation for TargetTime (RFC 3339) ✅ Done ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$
Add pattern validation for DestinationPath (s3://) ✅ Done ^s3://[a-z0-9][a-z0-9.\-]*[a-z0-9](/.*)?$
Extract shared S3Config type to reduce duplication ✅ Done New S3Config embedded type with json:",inline"
Define CompressionType alias for type safety ✅ Done CompressionType string type with constants

All critical issues, important issues, and suggestions are now complete.

@birdmanmandbir

Copy link
Copy Markdown
Contributor Author

Remain Issues:
Important (consider fixing)

Issue Description Location
No cleanup on disable When backup.enabled: false, ObjectStore/ScheduledBackup remain orphaned controller.go:711-714
Unreachable nil checks Controller nil checks after builder guard clauses are dead code controller.go:721, objectstore.go:49

Minor (acceptable)

Issue Description
TargetTime future date Can't validate targetTime isn't in future at CRD level (CNPG will catch it)
Error chain in condition err.Error() loses wrapped context (low impact)

- Add ConditionTypeRecoveryReady for recovery infrastructure status
- Add status condition updates to reconcileRecovery (was missing)
- Add failRecovery helper for consistent error handling
- Add validateS3Secret to verify credentials secret exists with
  required keys (ACCESS_KEY_ID, SECRET_ACCESS_KEY) before creating
  ObjectStore resources
- Apply S3 validation to both backup and recovery paths
@birdmanmandbir

Copy link
Copy Markdown
Contributor Author

Additional Fixes (648e2fb)

Two critical issues identified in second review have been fixed:

Issue Status Fix
Missing recovery status condition ✅ Fixed Added ConditionTypeRecoveryReady + failRecovery helper
Missing S3 secret validation ✅ Fixed Added validateS3Secret for both backup and recovery paths

S3 Secret Validation

Before creating ObjectStore, now validates:

  • Secret exists in namespace
  • Secret has ACCESS_KEY_ID key
  • Secret has SECRET_ACCESS_KEY key

Users will see clear error in status condition if S3 secret is missing or invalid.

- Add cleanupBackupResources to delete ObjectStore/ScheduledBackup when disabled
- Add cleanupRecoveryResources to delete recovery ObjectStore when disabled
- Skip cleanup if feature was never configured (check condition existence)
- Remove unreachable nil checks after builder guard clauses
@birdmanmandbir

Copy link
Copy Markdown
Contributor Author

Additional fixes in 8c15c1e:

  • Cleanup on disable - Added cleanupBackupResources() and cleanupRecoveryResources() to properly delete ObjectStore/ScheduledBackup when backup/recovery is disabled
  • Optimized cleanup - Skip cleanup API calls if the feature was never configured (check for condition existence using meta.FindStatusCondition())
  • Removed unreachable nil checks - Deleted nil checks after builder calls since guard clauses ensure builders always return valid objects

- Split into pr.yaml (PR checks) and ci.yaml (main push)
- pr.yaml: lint, test, build, check generated manifests
- ci.yaml: lint, test, build, auto-sync CRDs to Helm chart, docker push
- Pin golangci-lint to v2.6 with action v9
- Remove stale BackupReady/RecoveryReady conditions on cleanup
@birdmanmandbir

Copy link
Copy Markdown
Contributor Author

Latest fixes in cc55f8c:

Workflow refactor:

  • Split ci.yaml into pr.yaml (PR checks) and ci.yaml (main push)
  • PR workflow checks generated manifests are up to date
  • CI workflow auto-syncs CRDs to Helm chart on merge
  • Pinned golangci-lint to v2.6 with action v9

Controller fix:

  • Remove stale BackupReady/RecoveryReady conditions when cleanup runs

@birdmanmandbir
birdmanmandbir merged commit f0c70b9 into main Jan 3, 2026
2 checks passed
@birdmanmandbir
birdmanmandbir deleted the feat/backup-recovery-crd-types branch January 3, 2026 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant