diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index e912ee09e..7487f2b84 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -29,6 +29,7 @@ import ( "github.com/sirupsen/logrus" apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/workqueue" @@ -44,6 +45,10 @@ import ( kubeutil "github.com/heptio/ark/pkg/util/kube" ) +// nonRestorableResources is a blacklist for the restoration process. Any resources +// included here are explicitly excluded from the restoration process. +var nonRestorableResources = []string{"nodes"} + type restoreController struct { restoreClient arkv1client.RestoresGetter backupClient arkv1client.BackupsGetter @@ -234,6 +239,13 @@ func (controller *restoreController) processRestore(key string) error { restore.Spec.IncludedResources = []string{"*"} } + excludedResources := sets.NewString(restore.Spec.ExcludedResources...) + for _, nonrestorable := range nonRestorableResources { + if !excludedResources.Has(nonrestorable) { + restore.Spec.ExcludedResources = append(restore.Spec.ExcludedResources, nonrestorable) + } + } + // validation if restore.Status.ValidationErrors = controller.getValidationErrors(restore); len(restore.Status.ValidationErrors) > 0 { restore.Status.Phase = api.RestorePhaseFailedValidation @@ -288,6 +300,13 @@ func (controller *restoreController) getValidationErrors(itm *api.Restore) []str validationErrors = append(validationErrors, "BackupName must be non-empty and correspond to the name of a backup in object storage.") } + includedResources := sets.NewString(itm.Spec.IncludedResources...) + for _, nonRestorableResource := range nonRestorableResources { + if includedResources.Has(nonRestorableResource) { + validationErrors = append(validationErrors, fmt.Sprintf("%v are a non-restorable resource", nonRestorableResource)) + } + } + for _, err := range collections.ValidateIncludesExcludes(itm.Spec.IncludedNamespaces, itm.Spec.ExcludedNamespaces) { validationErrors = append(validationErrors, fmt.Sprintf("Invalid included/excluded namespace lists: %v", err)) } diff --git a/pkg/controller/restore_controller_test.go b/pkg/controller/restore_controller_test.go index 96dddef88..d7068a491 100644 --- a/pkg/controller/restore_controller_test.go +++ b/pkg/controller/restore_controller_test.go @@ -259,6 +259,18 @@ func TestProcessRestore(t *testing.T) { Restore, }, }, + { + name: "restoration of nodes is not supported", + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "nodes", api.RestorePhaseNew).Restore, + backup: NewTestBackup().WithName("backup-1").Backup, + expectedErr: false, + expectedRestoreUpdates: []*api.Restore{ + NewRestore("foo", "bar", "backup-1", "ns-1", "nodes", api.RestorePhaseFailedValidation). + WithValidationError("nodes are a non-restorable resource"). + WithValidationError("Invalid included/excluded resource lists: excludes list cannot contain an item in the includes list: nodes"). + Restore, + }, + }, } for _, test := range tests { @@ -382,6 +394,10 @@ func NewRestore(ns, name, backup, includeNS, includeResource string, phase api.R restore = restore.WithIncludedResource(includeResource) } + for _, n := range nonRestorableResources { + restore.WithExcludedResource(n) + } + return restore }