mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-08-20 14:16:06 +00:00
Implement default resource modifier in restore controller
Thread DefaultResourceModifierConfigMap from server config through to restoreReconciler. Refactor validateAndComplete to use a shared loadResourceModifierConfigMap helper that handles both default and per-restore ConfigMap loading. Precedence: per-restore modifier takes exclusive precedence over the default. Default ConfigMap errors are non-fatal (warn and proceed). SkipDefaultResourceModifier opt-out is respected. Includes unit tests covering: default-only, per-restore override, skip flag, missing default (non-fatal), missing per-restore (fatal), and no modifier configured. Signed-off-by: Shubham Pampattiwar <spampatt@redhat.com>
This commit is contained in:
@@ -116,6 +116,7 @@ func TestFetchBackupInfo(t *testing.T) {
|
||||
false,
|
||||
fakeGlobalClient,
|
||||
10*time.Minute,
|
||||
"",
|
||||
)
|
||||
|
||||
if test.backupStoreError == nil {
|
||||
@@ -197,6 +198,7 @@ func TestProcessQueueItemSkips(t *testing.T) {
|
||||
false,
|
||||
fakeGlobalClient,
|
||||
10*time.Minute,
|
||||
"",
|
||||
)
|
||||
|
||||
_, err := r.Reconcile(t.Context(), ctrl.Request{NamespacedName: types.NamespacedName{
|
||||
@@ -579,6 +581,7 @@ func TestRestoreReconcile(t *testing.T) {
|
||||
false,
|
||||
fakeGlobalClient,
|
||||
10*time.Minute,
|
||||
"",
|
||||
)
|
||||
|
||||
r.clock = clocktesting.NewFakeClock(now)
|
||||
@@ -767,6 +770,7 @@ func TestValidateAndCompleteWhenScheduleNameSpecified(t *testing.T) {
|
||||
false,
|
||||
fakeGlobalClient,
|
||||
10*time.Minute,
|
||||
"",
|
||||
)
|
||||
|
||||
restore := &velerov1api.Restore{
|
||||
@@ -863,6 +867,7 @@ func TestValidateAndCompleteWithResourcePolicySpecified(t *testing.T) {
|
||||
false,
|
||||
fakeGlobalClient,
|
||||
10*time.Minute,
|
||||
"",
|
||||
)
|
||||
|
||||
restore := &velerov1api.Restore{
|
||||
@@ -992,6 +997,7 @@ func TestValidateAndCompleteWithResourceModifierSpecified(t *testing.T) {
|
||||
false,
|
||||
fakeGlobalClient,
|
||||
10*time.Minute,
|
||||
"",
|
||||
)
|
||||
|
||||
restore := &velerov1api.Restore{
|
||||
@@ -1110,6 +1116,139 @@ func TestValidateAndCompleteWithResourceModifierSpecified(t *testing.T) {
|
||||
assert.Contains(t, restore3.Status.ValidationErrors[0], "Validation error in resource modifiers provided in configmap")
|
||||
}
|
||||
|
||||
func TestValidateAndCompleteWithDefaultResourceModifier(t *testing.T) {
|
||||
formatFlag := logging.FormatText
|
||||
|
||||
validCMData := map[string]string{
|
||||
"modifiers.yaml": "version: v1\nresourceModifierRules:\n- conditions:\n groupResource: pods\n mergePatches:\n - patchData: |\n metadata:\n annotations:\n k8s.ovn.org/pod-networks: null\n",
|
||||
}
|
||||
|
||||
setupReconciler := func(t *testing.T, defaultCM string) *restoreReconciler {
|
||||
t.Helper()
|
||||
fakeClient := velerotest.NewFakeControllerRuntimeClient(t)
|
||||
fakeGlobalClient := velerotest.NewFakeControllerRuntimeClient(t)
|
||||
pluginManager := &pluginmocks.Manager{}
|
||||
backupStore := &persistencemocks.BackupStore{}
|
||||
|
||||
r := NewRestoreReconciler(
|
||||
t.Context(),
|
||||
velerov1api.DefaultNamespace,
|
||||
nil,
|
||||
fakeClient,
|
||||
velerotest.NewLogger(),
|
||||
logrus.DebugLevel,
|
||||
func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager },
|
||||
NewFakeSingleObjectBackupStoreGetter(backupStore),
|
||||
metrics.NewServerMetrics(),
|
||||
formatFlag,
|
||||
60*time.Minute,
|
||||
false,
|
||||
fakeGlobalClient,
|
||||
10*time.Minute,
|
||||
defaultCM,
|
||||
)
|
||||
|
||||
location := builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Phase(velerov1api.BackupStorageLocationPhaseAvailable).Result()
|
||||
require.NoError(t, r.kbClient.Create(t.Context(), location))
|
||||
require.NoError(t, r.kbClient.Create(t.Context(),
|
||||
defaultBackup().ObjectMeta(builder.WithName("backup-1")).StorageLocation("default").Phase(velerov1api.BackupPhaseCompleted).Result(),
|
||||
))
|
||||
return r
|
||||
}
|
||||
|
||||
newRestore := func(perRestoreCM string, skip *bool) *velerov1api.Restore {
|
||||
restore := &velerov1api.Restore{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
Name: "restore-1",
|
||||
},
|
||||
Spec: velerov1api.RestoreSpec{
|
||||
BackupName: "backup-1",
|
||||
SkipDefaultResourceModifier: skip,
|
||||
},
|
||||
}
|
||||
if perRestoreCM != "" {
|
||||
restore.Spec.ResourceModifier = &corev1api.TypedLocalObjectReference{
|
||||
Kind: resourcemodifiers.ConfigmapRefType,
|
||||
Name: perRestoreCM,
|
||||
}
|
||||
}
|
||||
return restore
|
||||
}
|
||||
|
||||
t.Run("default modifier applied when no per-restore modifier", func(t *testing.T) {
|
||||
r := setupReconciler(t, "default-rm")
|
||||
require.NoError(t, r.kbClient.Create(t.Context(), &corev1api.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "default-rm", Namespace: velerov1api.DefaultNamespace},
|
||||
Data: validCMData,
|
||||
}))
|
||||
|
||||
restore := newRestore("", nil)
|
||||
_, rm, _ := r.validateAndComplete(t.Context(), restore)
|
||||
assert.NotNil(t, rm)
|
||||
assert.Empty(t, restore.Status.ValidationErrors)
|
||||
})
|
||||
|
||||
t.Run("per-restore modifier takes exclusive precedence", func(t *testing.T) {
|
||||
r := setupReconciler(t, "default-rm")
|
||||
require.NoError(t, r.kbClient.Create(t.Context(), &corev1api.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "default-rm", Namespace: velerov1api.DefaultNamespace},
|
||||
Data: validCMData,
|
||||
}))
|
||||
require.NoError(t, r.kbClient.Create(t.Context(), &corev1api.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "per-restore-rm", Namespace: velerov1api.DefaultNamespace},
|
||||
Data: validCMData,
|
||||
}))
|
||||
|
||||
restore := newRestore("per-restore-rm", nil)
|
||||
_, rm, _ := r.validateAndComplete(t.Context(), restore)
|
||||
assert.NotNil(t, rm)
|
||||
assert.Empty(t, restore.Status.ValidationErrors)
|
||||
})
|
||||
|
||||
t.Run("skip default modifier when SkipDefaultResourceModifier is true", func(t *testing.T) {
|
||||
r := setupReconciler(t, "default-rm")
|
||||
require.NoError(t, r.kbClient.Create(t.Context(), &corev1api.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "default-rm", Namespace: velerov1api.DefaultNamespace},
|
||||
Data: validCMData,
|
||||
}))
|
||||
|
||||
skipTrue := true
|
||||
restore := newRestore("", &skipTrue)
|
||||
_, rm, _ := r.validateAndComplete(t.Context(), restore)
|
||||
assert.Nil(t, rm)
|
||||
assert.Empty(t, restore.Status.ValidationErrors)
|
||||
})
|
||||
|
||||
t.Run("default modifier missing is non-fatal", func(t *testing.T) {
|
||||
r := setupReconciler(t, "nonexistent-cm")
|
||||
|
||||
restore := newRestore("", nil)
|
||||
_, rm, _ := r.validateAndComplete(t.Context(), restore)
|
||||
assert.Nil(t, rm)
|
||||
assert.Empty(t, restore.Status.ValidationErrors)
|
||||
})
|
||||
|
||||
t.Run("per-restore modifier missing is fatal", func(t *testing.T) {
|
||||
r := setupReconciler(t, "")
|
||||
|
||||
restore := newRestore("nonexistent-cm", nil)
|
||||
_, rm, _ := r.validateAndComplete(t.Context(), restore)
|
||||
assert.Nil(t, rm)
|
||||
assert.NotEmpty(t, restore.Status.ValidationErrors)
|
||||
assert.Contains(t, restore.Status.ValidationErrors[0], "failed to get resource modifiers configmap")
|
||||
})
|
||||
|
||||
t.Run("no default configured and no per-restore modifier", func(t *testing.T) {
|
||||
r := setupReconciler(t, "")
|
||||
|
||||
restore := newRestore("", nil)
|
||||
_, rm, _ := r.validateAndComplete(t.Context(), restore)
|
||||
assert.Nil(t, rm)
|
||||
assert.Empty(t, restore.Status.ValidationErrors)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBackupXorScheduleProvided(t *testing.T) {
|
||||
r := &velerov1api.Restore{}
|
||||
assert.False(t, backupXorScheduleProvided(r))
|
||||
|
||||
Reference in New Issue
Block a user