Skip to content

Commit

Permalink
add --include-cluster-resources flag to restores
Browse files Browse the repository at this point in the history
Signed-off-by: Steve Kriss <steve@heptio.com>
  • Loading branch information
skriss committed Oct 20, 2017
1 parent 568a18e commit 9c4e15a
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 40 deletions.
1 change: 1 addition & 0 deletions docs/cli-reference/ark_create_restore.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ ark create restore BACKUP [flags]
--exclude-namespaces stringArray namespaces to exclude from the restore
--exclude-resources stringArray resources to exclude from the restore, formatted as resource.group, such as storageclasses.storage.k8s.io
-h, --help help for restore
--include-cluster-resources include cluster-scoped resources in the restore (default true)
--include-namespaces stringArray namespaces to include in the restore (use '*' for all namespaces) (default *)
--include-resources stringArray resources to include in the restore, formatted as resource.group, such as storageclasses.storage.k8s.io (use '*' for all resources)
--label-columns stringArray a comma-separated list of labels to be displayed as columns
Expand Down
1 change: 1 addition & 0 deletions docs/cli-reference/ark_restore_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ ark restore create BACKUP [flags]
--exclude-namespaces stringArray namespaces to exclude from the restore
--exclude-resources stringArray resources to exclude from the restore, formatted as resource.group, such as storageclasses.storage.k8s.io
-h, --help help for create
--include-cluster-resources include cluster-scoped resources in the restore (default true)
--include-namespaces stringArray namespaces to include in the restore (use '*' for all namespaces) (default *)
--include-resources stringArray resources to include in the restore, formatted as resource.group, such as storageclasses.storage.k8s.io (use '*' for all resources)
--label-columns stringArray a comma-separated list of labels to be displayed as columns
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/ark/v1/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ type RestoreSpec struct {
// RestorePVs specifies whether to restore all included
// PVs from snapshot (via the cloudprovider).
RestorePVs *bool `json:"restorePVs"`

// IncludeClusterResources specifies whether cluster-scoped resources
// should be included for consideration in the restore.
IncludeClusterResources bool `json:"includeClusterResources"`
}

// RestorePhase is a string representation of the lifecycle phase
Expand Down
47 changes: 26 additions & 21 deletions pkg/cmd/cli/restore/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,23 +54,25 @@ func NewCreateCommand(f client.Factory, use string) *cobra.Command {
}

type CreateOptions struct {
BackupName string
RestoreVolumes flag.OptionalBool
Labels flag.Map
IncludeNamespaces flag.StringArray
ExcludeNamespaces flag.StringArray
IncludeResources flag.StringArray
ExcludeResources flag.StringArray
NamespaceMappings flag.Map
Selector flag.LabelSelector
BackupName string
RestoreVolumes flag.OptionalBool
Labels flag.Map
IncludeNamespaces flag.StringArray
ExcludeNamespaces flag.StringArray
IncludeResources flag.StringArray
ExcludeResources flag.StringArray
NamespaceMappings flag.Map
Selector flag.LabelSelector
IncludeClusterResources bool
}

func NewCreateOptions() *CreateOptions {
return &CreateOptions{
Labels: flag.NewMap(),
IncludeNamespaces: flag.NewStringArray("*"),
NamespaceMappings: flag.NewMap().WithEntryDelimiter(",").WithKeyValueDelimiter(":"),
RestoreVolumes: flag.NewOptionalBool(nil),
Labels: flag.NewMap(),
IncludeNamespaces: flag.NewStringArray("*"),
NamespaceMappings: flag.NewMap().WithEntryDelimiter(",").WithKeyValueDelimiter(":"),
RestoreVolumes: flag.NewOptionalBool(nil),
IncludeClusterResources: true,
}
}

Expand All @@ -86,6 +88,8 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) {
// this allows the user to just specify "--restore-volumes" as shorthand for "--restore-volumes=true"
// like a normal bool flag
f.NoOptDefVal = "true"

flags.BoolVar(&o.IncludeClusterResources, "include-cluster-resources", o.IncludeClusterResources, "include cluster-scoped resources in the restore")
}

func (o *CreateOptions) Validate(c *cobra.Command, args []string) error {
Expand Down Expand Up @@ -118,14 +122,15 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error {
Labels: o.Labels.Data(),
},
Spec: api.RestoreSpec{
BackupName: o.BackupName,
IncludedNamespaces: o.IncludeNamespaces,
ExcludedNamespaces: o.ExcludeNamespaces,
IncludedResources: o.IncludeResources,
ExcludedResources: o.ExcludeResources,
NamespaceMapping: o.NamespaceMappings.Data(),
LabelSelector: o.Selector.LabelSelector,
RestorePVs: o.RestoreVolumes.Value,
BackupName: o.BackupName,
IncludedNamespaces: o.IncludeNamespaces,
ExcludedNamespaces: o.ExcludeNamespaces,
IncludedResources: o.IncludeResources,
ExcludedResources: o.ExcludeResources,
NamespaceMapping: o.NamespaceMappings.Data(),
LabelSelector: o.Selector.LabelSelector,
RestorePVs: o.RestoreVolumes.Value,
IncludeClusterResources: o.IncludeClusterResources,
},
}

Expand Down
5 changes: 5 additions & 0 deletions pkg/restore/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,11 @@ func addToResult(r *api.RestoreResult, ns string, e error) {
func (ctx *context) restoreResource(resource, namespace, resourcePath string) (api.RestoreResult, api.RestoreResult) {
warnings, errs := api.RestoreResult{}, api.RestoreResult{}

if !ctx.restore.Spec.IncludeClusterResources && namespace == "" {
ctx.infof("Skipping resource %s because it's cluster-scoped", resource)
return warnings, errs
}

if namespace != "" {
ctx.infof("Restoring resource '%s' into namespace '%s' from: %s", resource, namespace, resourcePath)
} else {
Expand Down
66 changes: 47 additions & 19 deletions pkg/restore/restore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func TestRestoreNamespaceFiltering(t *testing.T) {
name: "namespacesToRestore having * restores all namespaces",
fileSystem: newFakeFileSystem().WithDirectories("bak/resources/nodes/cluster", "bak/resources/secrets/namespaces/a", "bak/resources/secrets/namespaces/b", "bak/resources/secrets/namespaces/c"),
baseDir: "bak",
restore: &api.Restore{Spec: api.RestoreSpec{IncludedNamespaces: []string{"*"}}},
restore: &api.Restore{Spec: api.RestoreSpec{IncludeClusterResources: true, IncludedNamespaces: []string{"*"}}},
expectedReadDirs: []string{"bak/resources", "bak/resources/nodes/cluster", "bak/resources/secrets/namespaces", "bak/resources/secrets/namespaces/a", "bak/resources/secrets/namespaces/b", "bak/resources/secrets/namespaces/c"},
prioritizedResources: []schema.GroupResource{
schema.GroupResource{Resource: "nodes"},
Expand All @@ -140,7 +140,7 @@ func TestRestoreNamespaceFiltering(t *testing.T) {
name: "namespacesToRestore properly filters",
fileSystem: newFakeFileSystem().WithDirectories("bak/resources/nodes/cluster", "bak/resources/secrets/namespaces/a", "bak/resources/secrets/namespaces/b", "bak/resources/secrets/namespaces/c"),
baseDir: "bak",
restore: &api.Restore{Spec: api.RestoreSpec{IncludedNamespaces: []string{"b", "c"}}},
restore: &api.Restore{Spec: api.RestoreSpec{IncludeClusterResources: true, IncludedNamespaces: []string{"b", "c"}}},
expectedReadDirs: []string{"bak/resources", "bak/resources/nodes/cluster", "bak/resources/secrets/namespaces", "bak/resources/secrets/namespaces/b", "bak/resources/secrets/namespaces/c"},
prioritizedResources: []schema.GroupResource{
schema.GroupResource{Resource: "nodes"},
Expand All @@ -151,18 +151,24 @@ func TestRestoreNamespaceFiltering(t *testing.T) {
name: "namespacesToRestore properly filters with exclusion filter",
fileSystem: newFakeFileSystem().WithDirectories("bak/resources/nodes/cluster", "bak/resources/secrets/namespaces/a", "bak/resources/secrets/namespaces/b", "bak/resources/secrets/namespaces/c"),
baseDir: "bak",
restore: &api.Restore{Spec: api.RestoreSpec{IncludedNamespaces: []string{"*"}, ExcludedNamespaces: []string{"a"}}},
restore: &api.Restore{Spec: api.RestoreSpec{IncludeClusterResources: true, IncludedNamespaces: []string{"*"}, ExcludedNamespaces: []string{"a"}}},
expectedReadDirs: []string{"bak/resources", "bak/resources/nodes/cluster", "bak/resources/secrets/namespaces", "bak/resources/secrets/namespaces/b", "bak/resources/secrets/namespaces/c"},
prioritizedResources: []schema.GroupResource{
schema.GroupResource{Resource: "nodes"},
schema.GroupResource{Resource: "secrets"},
},
},
{
name: "namespacesToRestore properly filters with inclusion & exclusion filters",
fileSystem: newFakeFileSystem().WithDirectories("bak/resources/nodes/cluster", "bak/resources/secrets/namespaces/a", "bak/resources/secrets/namespaces/b", "bak/resources/secrets/namespaces/c"),
baseDir: "bak",
restore: &api.Restore{Spec: api.RestoreSpec{IncludedNamespaces: []string{"a", "b", "c"}, ExcludedNamespaces: []string{"b"}}},
name: "namespacesToRestore properly filters with inclusion & exclusion filters",
fileSystem: newFakeFileSystem().WithDirectories("bak/resources/nodes/cluster", "bak/resources/secrets/namespaces/a", "bak/resources/secrets/namespaces/b", "bak/resources/secrets/namespaces/c"),
baseDir: "bak",
restore: &api.Restore{
Spec: api.RestoreSpec{
IncludeClusterResources: true,
IncludedNamespaces: []string{"a", "b", "c"},
ExcludedNamespaces: []string{"b"},
},
},
expectedReadDirs: []string{"bak/resources", "bak/resources/nodes/cluster", "bak/resources/secrets/namespaces", "bak/resources/secrets/namespaces/a", "bak/resources/secrets/namespaces/c"},
prioritizedResources: []schema.GroupResource{
schema.GroupResource{Resource: "nodes"},
Expand Down Expand Up @@ -210,7 +216,7 @@ func TestRestorePriority(t *testing.T) {
name: "cluster test",
fileSystem: newFakeFileSystem().WithDirectory("bak/resources/a/cluster").WithDirectory("bak/resources/c/cluster"),
baseDir: "bak",
restore: &api.Restore{Spec: api.RestoreSpec{IncludedNamespaces: []string{"*"}}},
restore: &api.Restore{Spec: api.RestoreSpec{IncludeClusterResources: true, IncludedNamespaces: []string{"*"}}},
prioritizedResources: []schema.GroupResource{
schema.GroupResource{Resource: "a"},
schema.GroupResource{Resource: "b"},
Expand All @@ -221,7 +227,7 @@ func TestRestorePriority(t *testing.T) {
{
name: "resource priorities are applied",
fileSystem: newFakeFileSystem().WithDirectory("bak/resources/a/cluster").WithDirectory("bak/resources/c/cluster"),
restore: &api.Restore{Spec: api.RestoreSpec{IncludedNamespaces: []string{"*"}}},
restore: &api.Restore{Spec: api.RestoreSpec{IncludeClusterResources: true, IncludedNamespaces: []string{"*"}}},
baseDir: "bak",
prioritizedResources: []schema.GroupResource{
schema.GroupResource{Resource: "c"},
Expand All @@ -233,7 +239,7 @@ func TestRestorePriority(t *testing.T) {
{
name: "basic namespace",
fileSystem: newFakeFileSystem().WithDirectory("bak/resources/a/namespaces/ns-1").WithDirectory("bak/resources/c/namespaces/ns-1"),
restore: &api.Restore{Spec: api.RestoreSpec{IncludedNamespaces: []string{"*"}}},
restore: &api.Restore{Spec: api.RestoreSpec{IncludeClusterResources: true, IncludedNamespaces: []string{"*"}}},
baseDir: "bak",
prioritizedResources: []schema.GroupResource{
schema.GroupResource{Resource: "a"},
Expand All @@ -247,7 +253,7 @@ func TestRestorePriority(t *testing.T) {
fileSystem: newFakeFileSystem().
WithFile("bak/resources/a/namespaces/ns-1/invalid-json.json", []byte("invalid json")).
WithDirectory("bak/resources/c/namespaces/ns-1"),
restore: &api.Restore{Spec: api.RestoreSpec{IncludedNamespaces: []string{"*"}}},
restore: &api.Restore{Spec: api.RestoreSpec{IncludeClusterResources: true, IncludedNamespaces: []string{"*"}}},
baseDir: "bak",
prioritizedResources: []schema.GroupResource{
schema.GroupResource{Resource: "a"},
Expand Down Expand Up @@ -289,14 +295,15 @@ func TestRestorePriority(t *testing.T) {

func TestRestoreResourceForNamespace(t *testing.T) {
tests := []struct {
name string
namespace string
resourcePath string
labelSelector labels.Selector
fileSystem *fakeFileSystem
restorers map[schema.GroupResource]restorers.ResourceRestorer
expectedErrors api.RestoreResult
expectedObjs []unstructured.Unstructured
name string
namespace string
resourcePath string
labelSelector labels.Selector
excludeClusterResources bool
fileSystem *fakeFileSystem
restorers map[schema.GroupResource]restorers.ResourceRestorer
expectedErrors api.RestoreResult
expectedObjs []unstructured.Unstructured
}{
{
name: "basic normal case",
Expand Down Expand Up @@ -394,6 +401,24 @@ func TestRestoreResourceForNamespace(t *testing.T) {
restorers: map[schema.GroupResource]restorers.ResourceRestorer{schema.GroupResource{Resource: "foo-resource"}: newFakeCustomRestorer()},
expectedObjs: toUnstructured(newTestConfigMap().WithArkLabel("my-restore").ConfigMap),
},
// NEW
{
name: "cluster-scoped resources are skipped when IncludeClusterResources=false",
namespace: "",
resourcePath: "persistentvolumes",
labelSelector: labels.NewSelector(),
excludeClusterResources: true,
fileSystem: newFakeFileSystem().WithFile("persistentvolumes/pv-1.json", newTestConfigMap().ToJSON()),
},
{
name: "namespaced resources are not skipped when IncludeClusterResources=false",
namespace: "ns-1",
resourcePath: "configmaps",
labelSelector: labels.NewSelector(),
excludeClusterResources: true,
fileSystem: newFakeFileSystem().WithFile("configmaps/cm-1.json", newTestConfigMap().ToJSON()),
expectedObjs: toUnstructured(newTestConfigMap().WithArkLabel("my-restore").ConfigMap),
},
}

for _, test := range tests {
Expand All @@ -420,6 +445,9 @@ func TestRestoreResourceForNamespace(t *testing.T) {
Namespace: api.DefaultNamespace,
Name: "my-restore",
},
Spec: api.RestoreSpec{
IncludeClusterResources: !test.excludeClusterResources,
},
},
backup: &api.Backup{},
logger: log,
Expand Down

0 comments on commit 9c4e15a

Please sign in to comment.