diff --git a/changelogs/unreleased/4779-reasonerjt b/changelogs/unreleased/4779-reasonerjt new file mode 100644 index 000000000..869257e84 --- /dev/null +++ b/changelogs/unreleased/4779-reasonerjt @@ -0,0 +1 @@ +Ensure the restore hook applied to new namespace based on the mapping \ No newline at end of file diff --git a/internal/hook/item_hook_handler.go b/internal/hook/item_hook_handler.go index 5631b3359..96cb18bc6 100644 --- a/internal/hook/item_hook_handler.go +++ b/internal/hook/item_hook_handler.go @@ -104,6 +104,7 @@ func (i *InitContainerRestoreHookHandler) HandleRestoreHooks( groupResource schema.GroupResource, obj runtime.Unstructured, resourceRestoreHooks []ResourceRestoreHook, + namespaceMapping map[string]string, ) (runtime.Unstructured, error) { // We only support hooks on pods right now if groupResource != kuberesource.Pods { @@ -141,6 +142,13 @@ func (i *InitContainerRestoreHookHandler) HandleRestoreHooks( namespace := metadata.GetNamespace() labels := labels.Set(metadata.GetLabels()) + // Apply the hook according to the target namespace in which the pod will be restored + // more details see https://github.com/vmware-tanzu/velero/issues/4720 + if namespaceMapping != nil { + if n, ok := namespaceMapping[namespace]; ok { + namespace = n + } + } for _, rh := range resourceRestoreHooks { if !rh.Selector.applicableTo(groupResource, namespace, labels) { continue diff --git a/internal/hook/item_hook_handler_test.go b/internal/hook/item_hook_handler_test.go index c1704d365..267c413a1 100644 --- a/internal/hook/item_hook_handler_test.go +++ b/internal/hook/item_hook_handler_test.go @@ -1395,11 +1395,12 @@ func TestGetRestoreHooksFromSpec(t *testing.T) { func TestHandleRestoreHooks(t *testing.T) { testCases := []struct { - name string - podInput corev1api.Pod - restoreHooks []ResourceRestoreHook - expectedPod *corev1api.Pod - expectedError error + name string + podInput corev1api.Pod + restoreHooks []ResourceRestoreHook + namespaceMapping map[string]string + expectedPod *corev1api.Pod + expectedError error }{ { name: "should handle hook from annotation no hooks in spec on pod with no init containers", @@ -1840,6 +1841,87 @@ func TestHandleRestoreHooks(t *testing.T) { }, restoreHooks: []ResourceRestoreHook{}, }, + { + name: "should not apply init container when the namespace mapping is provided and the hook points to the original namespace", + podInput: corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "default", + }, + Spec: corev1api.PodSpec{}, + }, + expectedError: nil, + expectedPod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "default", + }, + Spec: corev1api.PodSpec{}, + }, + restoreHooks: []ResourceRestoreHook{ + { + Name: "hook1", + Selector: ResourceHookSelector{ + Namespaces: collections.NewIncludesExcludes().Includes("default"), + Resources: collections.NewIncludesExcludes().Includes(kuberesource.Pods.Resource), + }, + RestoreHooks: []velerov1api.RestoreResourceHook{ + { + Init: &velerov1api.InitRestoreHook{ + InitContainers: []corev1api.Container{ + *builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).Result(), + }, + }, + }, + }, + }, + }, + namespaceMapping: map[string]string{"default": "new"}, + }, + { + name: "should apply init container when the namespace mapping is provided and the hook points to the new namespace", + podInput: corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "default", + }, + Spec: corev1api.PodSpec{}, + }, + expectedError: nil, + expectedPod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "default", + }, + Spec: corev1api.PodSpec{ + InitContainers: []corev1api.Container{ + *builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).Result(), + }, + }, + }, + restoreHooks: []ResourceRestoreHook{ + { + Name: "hook1", + Selector: ResourceHookSelector{ + Namespaces: collections.NewIncludesExcludes().Includes("new"), + Resources: collections.NewIncludesExcludes().Includes(kuberesource.Pods.Resource), + }, + RestoreHooks: []velerov1api.RestoreResourceHook{ + { + Init: &velerov1api.InitRestoreHook{ + InitContainers: []corev1api.Container{ + *builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).Result(), + }, + }, + }, + }, + }, + }, + namespaceMapping: map[string]string{"default": "new"}, + }, } for _, tc := range testCases { @@ -1847,7 +1929,7 @@ func TestHandleRestoreHooks(t *testing.T) { handler := InitContainerRestoreHookHandler{} podMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&tc.podInput) assert.NoError(t, err) - actual, err := handler.HandleRestoreHooks(velerotest.NewLogger(), kuberesource.Pods, &unstructured.Unstructured{Object: podMap}, tc.restoreHooks) + actual, err := handler.HandleRestoreHooks(velerotest.NewLogger(), kuberesource.Pods, &unstructured.Unstructured{Object: podMap}, tc.restoreHooks, tc.namespaceMapping) assert.Equal(t, tc.expectedError, err) actualPod := new(corev1api.Pod) err = runtime.DefaultUnstructuredConverter.FromUnstructured(actual.UnstructuredContent(), actualPod) diff --git a/pkg/restore/init_restorehook_pod_action.go b/pkg/restore/init_restorehook_pod_action.go index e9cd33d35..f994d811d 100644 --- a/pkg/restore/init_restorehook_pod_action.go +++ b/pkg/restore/init_restorehook_pod_action.go @@ -49,11 +49,12 @@ func (a *InitRestoreHookPodAction) Execute(input *velero.RestoreItemActionExecut a.logger.Infof("Executing InitRestoreHookPodAction") // handle any init container restore hooks for the pod restoreHooks, err := hook.GetRestoreHooksFromSpec(&input.Restore.Spec.Hooks) + nsMapping := input.Restore.Spec.NamespaceMapping if err != nil { return nil, errors.WithStack(err) } hookHandler := hook.InitContainerRestoreHookHandler{} - postHooksItem, err := hookHandler.HandleRestoreHooks(a.logger, kuberesource.Pods, input.Item, restoreHooks) + postHooksItem, err := hookHandler.HandleRestoreHooks(a.logger, kuberesource.Pods, input.Item, restoreHooks, nsMapping) if err != nil { return nil, errors.WithStack(err) }