Repository: vmware-tanzu/velero (Go)

Issue:
ItemAction plugins for unresolvable resource types are run for all types
- Create a GKE cluster
- Deploy velero with the CSI plugin
- Deploy the `nginx-example`  from [velero/examples/nginx-with-pv](https://github.com/vmware-tanzu/velero/blob/master/examples/nginx-app/with-pv.yaml)
- Create a backup using `velero backup create b2 --wait --include-namespaces nginx-example`

This Fails with 
```
$ velero backup create b2 --wait --include-namespaces nginx-example
Backup request "b2" submitted successfully.
Waiting for backup to complete. You may safely press ctrl-c to stop waiting - your backup will continue in the background.
..
Backup completed with status: PartiallyFailed. You may check for more information using the commands `velero backup describe b2` and `velero backup logs b2`.
```

From the velero logs:
```
time="2020-04-20T21:53:01Z" level=info msg="Backing up item" backup=velero/b-2 group=v1 logSource="pkg/backup/item_backupper.go:169" name=nginx-deployment-f96b7fd86-vthjb.1607a557c797e013 namespace=nginx-example resource=events
time="2020-04-20T21:53:01Z" level=info msg="Executing custom action" backup=velero/b-2 group=v1 logSource="pkg/backup/item_backupper.go:330" name=nginx-deployment-f96b7fd86-vthjb.1607a557c797e013 namespace=nginx-example resource=events
time="2020-04-20T21:53:01Z" level=info msg="Executing VolumeSnapshotBackupItemAction" backup=velero/b-2 cmd=/plugins/velero-plugin-for-csi logSource="/go/src/velero-plugin-for-csi/internal/backup/volumesnapshot_action.go:51" pluginName=velero-plugin-for-csi
time="2020-04-20T21:53:01Z" level=info msg="1 errors encountered backup up item" backup=velero/b-2 group=v1 logSource="pkg/backup/resource_backupper.go:276" name=nginx-deployment-f96b7fd86-vthjb.1607a557c797e013 namespace=nginx-example resource=events
time="2020-04-20T21:53:01Z" level=error msg="Error backing up item" backup=velero/b-2 error="error executing custom action (groupResource=events, namespace=nginx-example, name=nginx-deployment-f96b7fd86-vthjb.1607a557c797e013): rpc error: code = Aborted desc = plugin panicked: runtime error: invalid memory address or nil pointer dereference" error.file="/github.com/vmware-tanzu/velero/pkg/backup/item_backupper.go:334" error.function="github.com/vmware-tanzu/velero/pkg/backup.(*defaultItemBackupper).executeActions" group=v1 logSource="pkg/backup/resource_backupper.go:280" name=nginx-deployment-f96b7fd86-vthjb.1607a557c797e013 namespace=nginx-example resource=events
```


Removing the CSI plugin from the velero deployment and rerunning the backup is successful
```
$ velero backup create b3 --wait --include-namespaces nginx-example
Backup request "b3" submitted successfully.
Waiting for backup to complete. You may safely press ctrl-c to stop waiting - your backup will continue in the background.
....
Backup completed with status: Completed. You may check for more information using the commands `velero backup describe b3` and `velero backup logs b3`.

```

cc @nrb  


Discussion:
@ashish-amarnath there's a similar plugin-related issue reported in https://github.com/vmware-tanzu/velero/issues/2374, where plugins are running for the wrong resources.

I *think* there may be a bug where if a plugin has an included/excluded resource defined, and that resource doesn't exist in discovery at the start of the backup, then the include/exclude is just ignored, and the plugin gets executed for basically every resource type.

In this case, it sounds like the CSI snapshot CRDs may not be defined in your cluster,  so the CSI plugins end up running for *all* resources.
@skriss Yes, that definitely sounds like what might be happening. I will look into the bug report that you linked.

Considering that we now have feature flags exposed to the plugins. How do y'all feel about not registering the plugins if CSI is not enabled.
This is what happens when the CRD isn't installed in the cluster and a backup is triggered:
1. In the [resolveActions](https://github.com/vmware-tanzu/velero/blob/master/pkg/backup/backup.go#L103) function the `AppliesTo` method of the plugin is called which returns an included resource type unknown to the discovery helper.
2. This resource type is passed to [getResourceIncludesExcludes](https://github.com/vmware-tanzu/velero/blob/master/pkg/backup/backup.go#L138) function where the map function which upon encountering an unknown resource returns `""`.

If none of the resource types returned by the plugin's `AppliesTo` function is known to the discovery helper, the `resourceIncludes` is empty which is interpreted as all the resources. Causing this plugin to be invoked for all the resource types.
:+1: thanks for looking into that @ashish-amarnath. I can work on a fix for this one.
transferring to the Velero repo since this is a bug in Velero core, not in the CSI plugin.
The easiest thing to do here is probably to change [this line](https://github.com/vmware-tanzu/velero/blob/237065d91fe3565814afe047ab5afc9738e97242/pkg/backup/backup.go#L145), so that if an entry in the includes/excludes list can't be resolved via discovery, we return it *as-is*, rather than returning an empty string. That way, even if none if the included resources can be resolved, we still have entries in the "includes" list, so we don't end up running the plugin for *all* resources.

I think there's potentially some more significant refactoring to be done here, but this should address the most problematic behavior for now.
I am going to reopen this as the same issue exists for the RestoreItemAction and DeleteItemAction plugins.
PR #3059  should fix this.



Patch:
diff --git a/changelogs/unreleased/2462-skriss b/changelogs/unreleased/2462-skriss
new file mode 100644
index 0000000000..fa60c24471
--- /dev/null
+++ b/changelogs/unreleased/2462-skriss
@@ -0,0 +1,1 @@
+bug fix: when a resource includes/excludes list contains unresolvable items, don't remove them from the list, so that the list doesn't inadvertently end up matching *all* resources.
diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go
index cbd0b2b6b0..4878f51519 100644
--- a/pkg/backup/backup.go
+++ b/pkg/backup/backup.go
@@ -142,7 +142,10 @@ func getResourceIncludesExcludes(helper discovery.Helper, includes, excludes []s
 		func(item string) string {
 			gvr, _, err := helper.ResourceFor(schema.ParseGroupResource(item).WithVersion(""))
 			if err != nil {
-				return ""
+				// If we can't resolve it, return it as-is. This prevents the generated
+				// includes-excludes list from including *everything*, if none of the includes
+				// can be resolved. ref. https://github.com/vmware-tanzu/velero/issues/2461
+				return item
 			}
 
 			gr := gvr.GroupResource()
diff --git a/pkg/util/collections/includes_excludes.go b/pkg/util/collections/includes_excludes.go
index 8bfe68205f..2fc9028eb2 100644
--- a/pkg/util/collections/includes_excludes.go
+++ b/pkg/util/collections/includes_excludes.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2017 the Velero contributors.
+Copyright 2017, 2020 the Velero contributors.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -172,6 +172,12 @@ func GenerateIncludesExcludes(includes, excludes []string, mapFunc func(string)
 	}
 
 	for _, item := range excludes {
+		// wildcards are invalid for excludes,
+		// so ignore them.
+		if item == "*" {
+			continue
+		}
+
 		key := mapFunc(item)
 		if key == "" {
 			continue


Test patch:
diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go
index 940fec4059..73039daa70 100644
--- a/pkg/backup/backup_test.go
+++ b/pkg/backup/backup_test.go
@@ -543,6 +543,23 @@ func TestBackupResourceFiltering(t *testing.T) {
 				"resources/pods/namespaces/zoo/raz.json",
 			},
 		},
+		{
+			name: "when all included resources are unresolvable, nothing is included",
+			backup: defaultBackup().
+				IncludedResources("unresolvable-1", "unresolvable-2").
+				Result(),
+			apiResources: []*test.APIResource{
+				test.Pods(
+					builder.ForPod("foo", "bar").Result(),
+					builder.ForPod("zoo", "raz").Result(),
+				),
+				test.Deployments(
+					builder.ForDeployment("foo", "bar").Result(),
+					builder.ForDeployment("zoo", "raz").Result(),
+				),
+			},
+			want: []string{},
+		},
 		{
 			name: "unresolvable excluded resources are ignored",
 			backup: defaultBackup().
@@ -563,6 +580,29 @@ func TestBackupResourceFiltering(t *testing.T) {
 				"resources/pods/namespaces/zoo/raz.json",
 			},
 		},
+		{
+			name: "when all excluded resources are unresolvable, nothing is excluded",
+			backup: defaultBackup().
+				IncludedResources("*").
+				ExcludedResources("unresolvable-1", "unresolvable-2").
+				Result(),
+			apiResources: []*test.APIResource{
+				test.Pods(
+					builder.ForPod("foo", "bar").Result(),
+					builder.ForPod("zoo", "raz").Result(),
+				),
+				test.Deployments(
+					builder.ForDeployment("foo", "bar").Result(),
+					builder.ForDeployment("zoo", "raz").Result(),
+				),
+			},
+			want: []string{
+				"resources/pods/namespaces/foo/bar.json",
+				"resources/pods/namespaces/zoo/raz.json",
+				"resources/deployments.apps/namespaces/foo/bar.json",
+				"resources/deployments.apps/namespaces/zoo/raz.json",
+			},
+		},
 		{
 			name:   "terminating resources are not backed up",
 			backup: defaultBackup().Result(),
@@ -1101,6 +1141,26 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) {
 				new(recordResourcesAction).ForNamespace("ns-2").ForResource("pods"):                   nil,
 			},
 		},
+		{
+			name: "action with a selector that has unresolvable resources doesn't run for any resources",
+			backup: defaultBackup().
+				Result(),
+			apiResources: []*test.APIResource{
+				test.Pods(
+					builder.ForPod("ns-1", "pod-1").Result(),
+				),
+				test.PVCs(
+					builder.ForPersistentVolumeClaim("ns-2", "pvc-2").Result(),
+				),
+				test.PVs(
+					builder.ForPersistentVolume("pv-1").Result(),
+					builder.ForPersistentVolume("pv-2").Result(),
+				),
+			},
+			actions: map[*recordResourcesAction][]string{
+				new(recordResourcesAction).ForResource("unresolvable"): nil,
+			},
+		},
 	}
 
 	for _, tc := range tests {