# autobackup-operator

A Kubernetes operator that watches for `backups.unkin.net/*` annotations on
**PersistentVolumeClaims** and **CloudNativePG Clusters** and provisions the S3
bucket + backup-schedule resources needed to back them up. No new CRDs — it is
purely annotation-driven on existing objects.

## Annotations

| Annotation | Meaning |
|---|---|
| `backups.unkin.net/schedule` | `@hourly` / `@daily` / `@weekly` (plus `@monthly`/`@yearly`) or a raw cron expression. Absence means "not managed". |
| `backups.unkin.net/destination` | A logical destination name resolved via the destinations ConfigMap. |
| `backups.unkin.net/purge-on-delete` | `"true"` opts into deleting the bucket stack when the schedule annotation is removed (default: retain bucket data). |

## What it does

For every annotated object the operator, in the object's own namespace and
owner-referenced to it:

1. Provisions a cephrgw (`ceph.unkin.net/v1alpha1`) `ObjectStoreUser`, `Bucket`
   (`versioning: true`, `retainOnDelete: true`) and read-write `BucketAccess`.
2. Waits for the `BucketAccess` to become `Ready` (requeues until then).
3. Then wires up the backup schedule:
   - **PVC → k8up** (`k8up.io/v1` `Schedule`): S3 backend from the cephrgw
     credential Secret, an auto-generated restic repo-password Secret, and the
     backup scoped to exactly this PVC via
     `spec.backup.volumes[].persistentVolumeClaim.claimName`. Sane prune
     retention (keepDaily 7, keepWeekly 4).
   - **CNPG Cluster** (`postgresql.cnpg.io/v1`): patches
     `spec.backup.barmanObjectStore` (idempotently; a user-set different
     `destinationPath` is left untouched with a Warning event) and creates a
     `ScheduledBackup` with a 6-field (seconds-first) cron.

## Destinations ConfigMap

The operator resolves `destination` names against a ConfigMap in its own
namespace (`autobackup-operator/autobackup-destinations` by default). See
`config/samples/destinations.yaml`.

## Deploy

```sh
kubectl apply -k config
```

The `config/kustomization.yaml` orders the `Namespace` first so the
ServiceAccount, RBAC and Deployment land in an existing namespace. RBAC covers
both the controllers' resources (`config/rbac/role.yaml`) and the leader-election
lease/events the manager needs when run with `--leader-elect`
(`config/rbac/leader_election_role.yaml`). Then create the destinations
ConfigMap (see `config/samples/destinations.yaml`).

## Schedule mapping

- **k8up** accepts cron nicknames natively and 5-field cron expressions; the
  value is validated and passed through.
- **CNPG** requires a 6-field (seconds-first) cron: nicknames are expanded and a
  5-field cron gains a leading `0` seconds field.

Invalid schedules surface as Warning events on the annotated object.

## Development

```sh
make build      # build the operator binary
make test       # unit + envtest controller tests (fetches envtest binaries)
make lint       # go vet
make generate   # regenerate config/rbac/role.yaml from kubebuilder markers
```

Dependencies are vendored so the container build and CI need no access to the
private `cephrgw-operator` module.

## Release

`make patch|minor|major` tags and pushes `vX.Y.Z`. A `v*` tag triggers the
Woodpecker `docker` pipeline, which builds and pushes
`artifactapi.k8s.syd1.au.unkin.net/docker-internal/autobackup-operator:<tag>`.
