patches/vitess-backup-schedule-test.go.txt
Rama main · réplica de lectura del repositorio original.
package vitessbackupschedule
import (
"context"
"testing"
"time"
kbatch "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
planetscalev2 "planetscale.dev/vitess-operator/pkg/apis/planetscale/v2"
"planetscale.dev/vitess-operator/pkg/operator/vttablet"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
)
func TestHakopodScheduledBackupRetention(t *testing.T) {
schedule := planetscalev2.VitessBackupSchedule{}
schedule.Spec.JobTimeoutMinutes = 60
strategy := planetscalev2.VitessBackupScheduleStrategy{ExtraFlags: map[string]string{"min_retention_time": "72h", "min_retention_count": "2", "min_backup_interval": "30m"}}
spec := &vttablet.BackupSpec{}
if err := configureScheduledBackup(spec, schedule, strategy); err != nil {
t.Fatal(err)
}
if spec.MinRetentionTime != 72*time.Hour || spec.MinRetentionCount != 2 || spec.MinBackupInterval != 30*time.Minute {
t.Fatal("retention flags were ignored")
}
for name, flags := range map[string]map[string]string{
"missing": {},
"unbounded": {"min_retention_time": "0", "min_retention_count": "2"},
"one-copy": {"min_retention_time": "72h", "min_retention_count": "1"},
"unknown": {"database-password": "secret"},
} {
t.Run(name, func(t *testing.T) {
strategy.ExtraFlags = flags
if configureScheduledBackup(&vttablet.BackupSpec{}, schedule, strategy) == nil {
t.Fatal("unsafe configuration accepted")
}
})
}
}
func TestHakopodScheduledBackupStorageRetryAndBounds(t *testing.T) {
scheme := runtime.NewScheme()
for _, add := range []func(*runtime.Scheme) error{corev1.AddToScheme, kbatch.AddToScheme, planetscalev2.SchemeBuilder.AddToScheme} {
if err := add(scheme); err != nil {
t.Fatal(err)
}
}
schedule := &planetscalev2.VitessBackupSchedule{ObjectMeta: metav1.ObjectMeta{Name: "member-recovery", Namespace: "database", UID: "schedule-one"}}
labels := map[string]string{planetscalev2.ClusterLabel: "database", planetscalev2.KeyspaceLabel: "app", planetscalev2.ShardLabel: "x-x", planetscalev2.ComponentLabel: planetscalev2.VtbackupComponentName}
job := &kbatch.Job{ObjectMeta: metav1.ObjectMeta{Name: "backup-current", Namespace: "database", Labels: labels}}
spec := &vttablet.Spec{Labels: labels, DataVolumePVCSpec: &corev1.PersistentVolumeClaimSpec{AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, Resources: corev1.VolumeResourceRequirements{Requests: corev1.ResourceList{corev1.ResourceStorage: resource.MustParse("1Gi")}}}}
claim := func(name string) *corev1.PersistentVolumeClaim {
pvc := vttablet.NewPVC(client.ObjectKey{Namespace: "database", Name: name}, spec)
if err := ctrl.SetControllerReference(schedule, pvc, scheme); err != nil {
t.Fatal(err)
}
return pvc
}
t.Run("retry-owned-claim", func(t *testing.T) {
c := fake.NewClientBuilder().WithScheme(scheme).Build()
r := &ReconcileVitessBackupsSchedule{client: c, scheme: scheme}
for range 2 {
if err := r.ensureScheduledBackupStorage(context.Background(), schedule, job, spec); err != nil {
t.Fatal(err)
}
}
var claims corev1.PersistentVolumeClaimList
if err := c.List(context.Background(), &claims); err != nil || len(claims.Items) != 1 {
t.Fatal("retry allocated another claim", err)
}
})
t.Run("foreign-claim", func(t *testing.T) {
pvc := claim(job.Name)
pvc.OwnerReferences[0].UID = "foreign-schedule"
c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(pvc).Build()
r := &ReconcileVitessBackupsSchedule{client: c, scheme: scheme}
if r.ensureScheduledBackupStorage(context.Background(), schedule, job, spec) == nil {
t.Fatal("accepted foreign claim")
}
})
t.Run("full-storage", func(t *testing.T) {
c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(claim("first"), claim("second")).Build()
r := &ReconcileVitessBackupsSchedule{client: c, scheme: scheme}
if r.ensureScheduledBackupStorage(context.Background(), schedule, job, spec) == nil {
t.Fatal("allocated a third claim")
}
})
t.Run("full-pods", func(t *testing.T) {
one := &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "one", Namespace: "database", Labels: labels}}
two := one.DeepCopy()
two.Name = "two"
c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(one, two).Build()
r := &ReconcileVitessBackupsSchedule{client: c, scheme: scheme}
if r.ensureScheduledBackupStorage(context.Background(), schedule, job, spec) == nil {
t.Fatal("allocated storage for a third pod")
}
})
t.Run("recover-crashed-job-creation", func(t *testing.T) {
pvc := claim("old-abandoned")
pvc.UID = "claim-one"
pvc.CreationTimestamp = metav1.NewTime(time.Now().Add(-3 * time.Hour))
c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(pvc).Build()
r := &ReconcileVitessBackupsSchedule{client: c, scheme: scheme}
if r.ensureScheduledBackupStorage(context.Background(), schedule, job, spec) == nil {
t.Fatal("did not wait for orphan cleanup")
}
if err := c.Get(context.Background(), client.ObjectKeyFromObject(pvc), &corev1.PersistentVolumeClaim{}); !apierrors.IsNotFound(err) {
t.Fatal("owned orphan claim remains", err)
}
if err := r.ensureScheduledBackupStorage(context.Background(), schedule, job, spec); err != nil {
t.Fatal(err)
}
})
}