9da206dc7c
PVCs and CloudNativePG Clusters need S3 buckets and backup schedules
provisioned consistently. This operator watches the
backups.unkin.net/{schedule,destination} annotations on those objects and
provisions everything needed to back them up, with no new CRDs.
- Add a PVC controller that provisions cephrgw ObjectStoreUser/Bucket/BucketAccess,
auto-generates a restic repo-password Secret and creates a k8up Schedule scoped
to the PVC via spec.backup.volumes[].persistentVolumeClaim.claimName.
- Add a CNPG Cluster controller that provisions the same bucket stack, idempotently
patches spec.backup.barmanObjectStore (leaving a user-set destinationPath alone
with a Warning event) and creates a ScheduledBackup.
- Resolve destinations through a ConfigMap lookup table; requeue until the
BucketAccess is Ready before creating schedule resources; own-reference created
resources and retain bucket data by default.
- Add schedule-mapping helpers (k8up 5-field/shortcut pass-through, CNPG 6-field
seconds-first) and deterministic, length-bounded name derivation.
- Add unit tests (schedule mapping, name derivation, destination resolution) and
envtest controller tests for both paths, wiring the external CRDs into envtest.
- Add kubebuilder-generated RBAC, a Dockerfile (distroless/nonroot), Woodpecker
lint/test/build pipelines and a tag-triggered image push to the artifactapi
docker-internal registry, plus a version-bump Makefile and deploy manifests.
88 lines
2.4 KiB
Go
88 lines
2.4 KiB
Go
package controller
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
cephv1 "git.unkin.net/unkin/cephrgw-operator/api/v1alpha1"
|
|
k8upv1 "github.com/k8up-io/k8up/v2/api/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
|
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
|
)
|
|
|
|
var (
|
|
testEnv *envtest.Environment
|
|
k8sClient client.Client
|
|
envtestReady bool
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
utilruntime.Must(cephv1.AddToScheme(clientgoscheme.Scheme))
|
|
utilruntime.Must(k8upv1.AddToScheme(clientgoscheme.Scheme))
|
|
|
|
testEnv = &envtest.Environment{
|
|
CRDDirectoryPaths: []string{filepath.Join("..", "..", "test", "crds")},
|
|
ErrorIfCRDPathMissing: true,
|
|
}
|
|
cfg, err := testEnv.Start()
|
|
if err != nil {
|
|
// envtest binaries (KUBEBUILDER_ASSETS) may be unavailable; run the
|
|
// pure-unit tests and skip the controller tests.
|
|
fmt.Fprintf(os.Stderr, "envtest unavailable, skipping controller tests: %v\n", err)
|
|
os.Exit(m.Run())
|
|
}
|
|
k8sClient, err = client.New(cfg, client.Options{Scheme: clientgoscheme.Scheme})
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "failed to build client: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
envtestReady = true
|
|
code := m.Run()
|
|
_ = testEnv.Stop()
|
|
os.Exit(code)
|
|
}
|
|
|
|
func requireEnvtest(t *testing.T) {
|
|
t.Helper()
|
|
if !envtestReady {
|
|
t.Skip("envtest not available (KUBEBUILDER_ASSETS unset)")
|
|
}
|
|
}
|
|
|
|
func newNamespace(t *testing.T, ctx context.Context) string {
|
|
t.Helper()
|
|
name := fmt.Sprintf("test-%s", randSuffix())
|
|
ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: name}}
|
|
if err := k8sClient.Create(ctx, ns); err != nil {
|
|
t.Fatalf("create namespace: %v", err)
|
|
}
|
|
return name
|
|
}
|
|
|
|
func createDestinations(t *testing.T, ctx context.Context, ns string) {
|
|
t.Helper()
|
|
cm := &corev1.ConfigMap{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "autobackup-destinations", Namespace: ns},
|
|
Data: map[string]string{
|
|
"cephs3_ec4_1": "placementTarget: cephs3_ec4_1\nzonegroup: au\nendpoint: https://s3.ceph.unkin.net\nendpointCASecret: vault-ca-cert\nendpointCAKey: ca.crt\n",
|
|
},
|
|
}
|
|
if err := k8sClient.Create(ctx, cm); err != nil {
|
|
t.Fatalf("create destinations configmap: %v", err)
|
|
}
|
|
}
|
|
|
|
var suffixCounter int
|
|
|
|
func randSuffix() string {
|
|
suffixCounter++
|
|
return fmt.Sprintf("%d", suffixCounter)
|
|
}
|