More namespace special casing

Signed-off-by: Andy Goldstein <andy.goldstein@gmail.com>
pull/182/head
Andy Goldstein 2017-11-03 16:07:51 -04:00
parent 9471f9da3c
commit efa4e57d1e
4 changed files with 298 additions and 330 deletions

View File

@ -103,6 +103,7 @@ type defaultItemBackupper struct {
}
var podsGroupResource = schema.GroupResource{Group: "", Resource: "pods"}
var namespacesGroupResource = schema.GroupResource{Group: "", Resource: "namespaces"}
// backupItem backs up an individual item to tarWriter. The item may be excluded based on the
// namespaces IncludesExcludes list.
@ -127,7 +128,9 @@ func (ib *defaultItemBackupper) backupItem(logger *logrus.Entry, obj runtime.Uns
return nil
}
if namespace == "" && ib.backup.Spec.IncludeClusterResources != nil && !*ib.backup.Spec.IncludeClusterResources {
// NOTE: we specifically allow namespaces to be backed up even if IncludeClusterResources is
// false.
if namespace == "" && groupResource != namespacesGroupResource && ib.backup.Spec.IncludeClusterResources != nil && !*ib.backup.Spec.IncludeClusterResources {
log.Info("Excluding item because resource is cluster-scoped and backup.spec.includeClusterResources is false")
return nil
}

View File

@ -103,6 +103,23 @@ func TestBackupItemSkips(t *testing.T) {
}
}
func TestBackupItemSkipsClusterScopedResourceWhenIncludeClusterResourcesFalse(t *testing.T) {
f := false
ib := &defaultItemBackupper{
backup: &v1.Backup{
Spec: v1.BackupSpec{
IncludeClusterResources: &f,
},
},
namespaces: collections.NewIncludesExcludes(),
resources: collections.NewIncludesExcludes(),
}
u := unstructuredOrDie(`{"apiVersion":"v1","kind":"Foo","metadata":{"name":"bar"}}`)
err := ib.backupItem(arktest.NewLogger(), u, schema.GroupResource{Group: "foo", Resource: "bar"})
assert.NoError(t, err)
}
func TestBackupItemNoSkips(t *testing.T) {
tests := []struct {
name string

View File

@ -25,6 +25,7 @@ import (
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
kuberrs "k8s.io/apimachinery/pkg/util/errors"
@ -122,26 +123,27 @@ func (rb *defaultResourceBackupper) backupResource(
log := rb.log.WithField("groupResource", grString)
switch {
case rb.backup.Spec.IncludeClusterResources == nil:
// when IncludeClusterResources == nil (auto), only directly
// back up cluster-scoped resources if we're doing a full-cluster
// (all namespaces) backup. Note that in the case of a subset of
// namespaces being backed up, some related cluster-scoped resources
// may still be backed up if triggered by a custom action (e.g. PVC->PV).
// If we're processing namespaces themselves, we will not skip here, they may be
// filtered out later.
if !resource.Namespaced && resource.Kind != "Namespace" && !rb.namespaces.IncludeEverything() {
log.Info("Skipping resource because it's cluster-scoped and only specific namespaces are included in the backup")
return nil
}
case *rb.backup.Spec.IncludeClusterResources == false:
if !resource.Namespaced {
clusterScoped := !resource.Namespaced
// If the resource we are backing up is NOT namespaces, and it is cluster-scoped, check to see if
// we should include it based on the IncludeClusterResources setting.
if gr != namespacesGroupResource && clusterScoped {
if rb.backup.Spec.IncludeClusterResources == nil {
if !rb.namespaces.IncludeEverything() {
// when IncludeClusterResources == nil (auto), only directly
// back up cluster-scoped resources if we're doing a full-cluster
// (all namespaces) backup. Note that in the case of a subset of
// namespaces being backed up, some related cluster-scoped resources
// may still be backed up if triggered by a custom action (e.g. PVC->PV).
// If we're processing namespaces themselves, we will not skip here, they may be
// filtered out later.
log.Info("Skipping resource because it's cluster-scoped and only specific namespaces are included in the backup")
return nil
}
} else if !*rb.backup.Spec.IncludeClusterResources {
log.Info("Skipping resource because it's cluster-scoped")
return nil
}
case *rb.backup.Spec.IncludeClusterResources == true:
// include the resource, no action required
}
if !rb.resources.ShouldInclude(grString) {
@ -175,14 +177,50 @@ func (rb *defaultResourceBackupper) backupResource(
rb.discoveryHelper,
)
// TODO: when processing namespaces, and only including certain namespaces, we still list
// them all here. Could optimize to get specifics, but watch out for label selector.
var namespacesToList []string
if resource.Namespaced {
namespacesToList = getNamespacesToList(rb.namespaces)
} else {
namespacesToList := getNamespacesToList(rb.namespaces)
// Check if we're backing up namespaces, and only certain ones
if gr == namespacesGroupResource && namespacesToList[0] != "" {
resourceClient, err := rb.dynamicFactory.ClientForGroupVersionResource(gv, resource, "")
if err != nil {
return err
}
var labelSelector labels.Selector
if rb.backup.Spec.LabelSelector != nil {
labelSelector, err = metav1.LabelSelectorAsSelector(rb.backup.Spec.LabelSelector)
if err != nil {
// This should never happen...
return errors.Wrap(err, "invalid label selector")
}
}
for _, ns := range namespacesToList {
unstructured, err := resourceClient.Get(ns, metav1.GetOptions{})
if err != nil {
errs = append(errs, errors.Wrap(err, "error getting namespace"))
continue
}
labels := labels.Set(unstructured.GetLabels())
if labelSelector != nil && !labelSelector.Matches(labels) {
log.WithField("name", unstructured.GetName()).Info("skipping item because it does not match the backup's label selector")
continue
}
if err := itemBackupper.backupItem(log, unstructured, gr); err != nil {
errs = append(errs, err)
}
}
return kuberrs.NewAggregate(errs)
}
// If we get here, we're backing up something other than namespaces
if clusterScoped {
namespacesToList = []string{""}
}
for _, namespace := range namespacesToList {
resourceClient, err := rb.dynamicFactory.ClientForGroupVersionResource(gv, resource, namespace)
if err != nil {
@ -207,6 +245,17 @@ func (rb *defaultResourceBackupper) backupResource(
continue
}
metadata, err := meta.Accessor(unstructured)
if err != nil {
errs = append(errs, errors.Wrapf(err, "unable to get a metadata accessor"))
continue
}
if gr == namespacesGroupResource && !rb.namespaces.ShouldInclude(metadata.GetName()) {
log.WithField("name", metadata.GetName()).Info("skipping namespace because it is excluded")
continue
}
if err := itemBackupper.backupItem(log, unstructured, gr); err != nil {
errs = append(errs, err)
}

View File

@ -24,6 +24,7 @@ import (
"github.com/heptio/ark/pkg/discovery"
"github.com/heptio/ark/pkg/util/collections"
arktest "github.com/heptio/ark/pkg/util/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -50,6 +51,7 @@ func TestBackupResource(t *testing.T) {
groupVersion schema.GroupVersion
groupResource schema.GroupResource
listResponses [][]*unstructured.Unstructured
getResponses []*unstructured.Unstructured
includeClusterResources *bool
}{
{
@ -197,20 +199,18 @@ func TestBackupResource(t *testing.T) {
},
{
name: "should include specified namespaces if backing up subset of namespaces and --include-cluster-resources=nil",
namespaces: collections.NewIncludesExcludes().Includes("ns-1"),
namespaces: collections.NewIncludesExcludes().Includes("ns-1", "ns-2"),
resources: collections.NewIncludesExcludes(),
includeClusterResources: nil,
expectedListedNamespaces: []string{"ns-1"},
expectedListedNamespaces: []string{"ns-1", "ns-2"},
apiGroup: v1Group,
apiResource: namespacesResource,
groupVersion: schema.GroupVersion{Group: "", Version: "v1"},
groupResource: schema.GroupResource{Group: "", Resource: "namespaces"},
expectSkip: false,
listResponses: [][]*unstructured.Unstructured{
{
unstructuredOrDie(`{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"ns-1"}}`),
unstructuredOrDie(`{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"ns-2"}}`),
},
getResponses: []*unstructured.Unstructured{
unstructuredOrDie(`{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"ns-1"}}`),
unstructuredOrDie(`{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"ns-2"}}`),
},
},
}
@ -289,27 +289,38 @@ func TestBackupResource(t *testing.T) {
discoveryHelper,
).Return(itemBackupper)
for i, namespace := range test.expectedListedNamespaces {
if len(test.listResponses) > 0 {
for i, namespace := range test.expectedListedNamespaces {
client := &arktest.FakeDynamicClient{}
defer client.AssertExpectations(t)
dynamicFactory.On("ClientForGroupVersionResource", test.groupVersion, test.apiResource, namespace).Return(client, nil)
list := &unstructured.UnstructuredList{
Items: []unstructured.Unstructured{},
}
for _, item := range test.listResponses[i] {
list.Items = append(list.Items, *item)
itemBackupper.On("backupItem", mock.AnythingOfType("*logrus.Entry"), item, test.groupResource).Return(nil)
}
client.On("List", metav1.ListOptions{LabelSelector: labelSelector}).Return(list, nil)
}
}
if len(test.getResponses) > 0 {
client := &arktest.FakeDynamicClient{}
defer client.AssertExpectations(t)
if test.groupResource.Resource == "namespaces" {
dynamicFactory.On("ClientForGroupVersionResource", test.groupVersion, test.apiResource, "").Return(client, nil)
} else {
dynamicFactory.On("ClientForGroupVersionResource", test.groupVersion, test.apiResource, namespace).Return(client, nil)
}
dynamicFactory.On("ClientForGroupVersionResource", test.groupVersion, test.apiResource, "").Return(client, nil)
list := &unstructured.UnstructuredList{
Items: []unstructured.Unstructured{},
}
for _, item := range test.listResponses[i] {
list.Items = append(list.Items, *item)
for i, namespace := range test.expectedListedNamespaces {
item := test.getResponses[i]
client.On("Get", namespace, metav1.GetOptions{}).Return(item, nil)
itemBackupper.On("backupItem", mock.AnythingOfType("*logrus.Entry"), item, test.groupResource).Return(nil)
}
client.On("List", metav1.ListOptions{LabelSelector: labelSelector}).Return(list, nil)
}
}
err := rb.backupResource(test.apiGroup, test.apiResource)
require.NoError(t, err)
})
@ -449,6 +460,180 @@ func TestBackupResourceCohabitation(t *testing.T) {
}
}
func TestBackupResourceOnlyIncludesSpecifiedNamespaces(t *testing.T) {
backup := &v1.Backup{}
namespaces := collections.NewIncludesExcludes().Includes("ns-1")
resources := collections.NewIncludesExcludes().Includes("*")
labelSelector := "foo=bar"
backedUpItems := map[itemKey]struct{}{}
dynamicFactory := &arktest.FakeDynamicFactory{}
defer dynamicFactory.AssertExpectations(t)
discoveryHelper := arktest.NewFakeDiscoveryHelper(true, nil)
cohabitatingResources := map[string]*cohabitatingResource{}
actions := map[schema.GroupResource]Action{}
resourceHooks := []resourceHook{}
podCommandExecutor := &mockPodCommandExecutor{}
defer podCommandExecutor.AssertExpectations(t)
tarWriter := &fakeTarWriter{}
rb := (&defaultResourceBackupperFactory{}).newResourceBackupper(
arktest.NewLogger(),
backup,
namespaces,
resources,
labelSelector,
dynamicFactory,
discoveryHelper,
backedUpItems,
cohabitatingResources,
actions,
podCommandExecutor,
tarWriter,
resourceHooks,
).(*defaultResourceBackupper)
itemBackupperFactory := &mockItemBackupperFactory{}
defer itemBackupperFactory.AssertExpectations(t)
rb.itemBackupperFactory = itemBackupperFactory
itemHookHandler := &mockItemHookHandler{}
defer itemHookHandler.AssertExpectations(t)
itemBackupper := &defaultItemBackupper{
backup: backup,
namespaces: namespaces,
resources: resources,
backedUpItems: backedUpItems,
actions: actions,
tarWriter: tarWriter,
resourceHooks: resourceHooks,
dynamicFactory: dynamicFactory,
discoveryHelper: discoveryHelper,
itemHookHandler: itemHookHandler,
}
itemBackupperFactory.On("newItemBackupper",
backup,
namespaces,
resources,
backedUpItems,
actions,
podCommandExecutor,
tarWriter,
resourceHooks,
dynamicFactory,
discoveryHelper,
).Return(itemBackupper)
client := &arktest.FakeDynamicClient{}
defer client.AssertExpectations(t)
coreV1Group := schema.GroupVersion{Group: "", Version: "v1"}
dynamicFactory.On("ClientForGroupVersionResource", coreV1Group, namespacesResource, "").Return(client, nil)
ns1 := unstructuredOrDie(`{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"ns-1"}}`)
client.On("Get", "ns-1", metav1.GetOptions{}).Return(ns1, nil)
itemHookHandler.On("handleHooks", mock.Anything, schema.GroupResource{Group: "", Resource: "namespaces"}, ns1, resourceHooks).Return(nil)
err := rb.backupResource(v1Group, namespacesResource)
require.NoError(t, err)
require.Len(t, tarWriter.headers, 1)
assert.Equal(t, "resources/namespaces/cluster/ns-1.json", tarWriter.headers[0].Name)
}
func TestBackupResourceListAllNamespacesExcludesCorrectly(t *testing.T) {
backup := &v1.Backup{}
namespaces := collections.NewIncludesExcludes().Excludes("ns-1")
resources := collections.NewIncludesExcludes().Includes("*")
labelSelector := "foo=bar"
backedUpItems := map[itemKey]struct{}{}
dynamicFactory := &arktest.FakeDynamicFactory{}
defer dynamicFactory.AssertExpectations(t)
discoveryHelper := arktest.NewFakeDiscoveryHelper(true, nil)
cohabitatingResources := map[string]*cohabitatingResource{}
actions := map[schema.GroupResource]Action{}
resourceHooks := []resourceHook{}
podCommandExecutor := &mockPodCommandExecutor{}
defer podCommandExecutor.AssertExpectations(t)
tarWriter := &fakeTarWriter{}
rb := (&defaultResourceBackupperFactory{}).newResourceBackupper(
arktest.NewLogger(),
backup,
namespaces,
resources,
labelSelector,
dynamicFactory,
discoveryHelper,
backedUpItems,
cohabitatingResources,
actions,
podCommandExecutor,
tarWriter,
resourceHooks,
).(*defaultResourceBackupper)
itemBackupperFactory := &mockItemBackupperFactory{}
defer itemBackupperFactory.AssertExpectations(t)
rb.itemBackupperFactory = itemBackupperFactory
itemHookHandler := &mockItemHookHandler{}
defer itemHookHandler.AssertExpectations(t)
itemBackupper := &mockItemBackupper{}
defer itemBackupper.AssertExpectations(t)
itemBackupperFactory.On("newItemBackupper",
backup,
namespaces,
resources,
backedUpItems,
actions,
podCommandExecutor,
tarWriter,
resourceHooks,
dynamicFactory,
discoveryHelper,
).Return(itemBackupper)
client := &arktest.FakeDynamicClient{}
defer client.AssertExpectations(t)
coreV1Group := schema.GroupVersion{Group: "", Version: "v1"}
dynamicFactory.On("ClientForGroupVersionResource", coreV1Group, namespacesResource, "").Return(client, nil)
ns1 := unstructuredOrDie(`{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"ns-1"}}`)
ns2 := unstructuredOrDie(`{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"ns-2"}}`)
list := &unstructured.UnstructuredList{
Items: []unstructured.Unstructured{*ns1, *ns2},
}
client.On("List", metav1.ListOptions{LabelSelector: labelSelector}).Return(list, nil)
itemBackupper.On("backupItem", mock.AnythingOfType("*logrus.Entry"), ns2, namespacesGroupResource).Return(nil)
err := rb.backupResource(v1Group, namespacesResource)
require.NoError(t, err)
}
type mockItemBackupperFactory struct {
mock.Mock
}
@ -478,289 +663,3 @@ func (ibf *mockItemBackupperFactory) newItemBackupper(
)
return args.Get(0).(ItemBackupper)
}
/*
func TestBackupResource2(t *testing.T) {
tests := []struct {
name string
resourceIncludesExcludes *collections.IncludesExcludes
resourceGroup string
resourceVersion string
resourceGV string
resourceName string
resourceNamespaced bool
namespaceIncludesExcludes *collections.IncludesExcludes
expectedListedNamespaces []string
lists []string
labelSelector string
actions map[string]Action
expectedActionIDs map[string][]string
deploymentsBackedUp bool
expectedDeploymentsBackedUp bool
networkPoliciesBackedUp bool
expectedNetworkPoliciesBackedUp bool
}{
{
name: "should not include resource",
resourceIncludesExcludes: collections.NewIncludesExcludes().Includes("pods"),
resourceGV: "v1",
resourceName: "secrets",
resourceNamespaced: true,
},
{
name: "should skip deployments.extensions if we've seen deployments.apps",
resourceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
resourceGV: "extensions/v1beta1",
resourceName: "deployments",
resourceNamespaced: true,
deploymentsBackedUp: true,
expectedDeploymentsBackedUp: true,
},
{
name: "should skip deployments.apps if we've seen deployments.extensions",
resourceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
resourceGV: "apps/v1beta1",
resourceName: "deployments",
resourceNamespaced: true,
deploymentsBackedUp: true,
expectedDeploymentsBackedUp: true,
},
{
name: "should skip networkpolicies.extensions if we've seen networkpolicies.networking.k8s.io",
resourceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
resourceGV: "extensions/v1beta1",
resourceName: "networkpolicies",
resourceNamespaced: true,
networkPoliciesBackedUp: true,
expectedNetworkPoliciesBackedUp: true,
},
{
name: "should skip networkpolicies.networking.k8s.io if we've seen networkpolicies.extensions",
resourceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
resourceGV: "networking.k8s.io/v1",
resourceName: "networkpolicies",
resourceNamespaced: true,
networkPoliciesBackedUp: true,
expectedNetworkPoliciesBackedUp: true,
},
{
name: "list per namespace when not including *",
resourceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
resourceGroup: "apps",
resourceVersion: "v1beta1",
resourceGV: "apps/v1beta1",
resourceName: "deployments",
resourceNamespaced: true,
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("a", "b"),
expectedListedNamespaces: []string{"a", "b"},
lists: []string{
`{
"apiVersion": "apps/v1beta1",
"kind": "DeploymentList",
"items": [
{
"metadata": {
"namespace": "a",
"name": "1"
}
}
]
}`,
`{
"apiVersion": "apps/v1beta1v1",
"kind": "DeploymentList",
"items": [
{
"metadata": {
"namespace": "b",
"name": "2"
}
}
]
}`,
},
expectedDeploymentsBackedUp: true,
},
{
name: "list all namespaces when including *",
resourceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
resourceGroup: "networking.k8s.io",
resourceVersion: "v1",
resourceGV: "networking.k8s.io/v1",
resourceName: "networkpolicies",
resourceNamespaced: true,
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
expectedListedNamespaces: []string{""},
lists: []string{
`{
"apiVersion": "networking.k8s.io/v1",
"kind": "NetworkPolicyList",
"items": [
{
"metadata": {
"namespace": "a",
"name": "1"
}
}
]
}`,
},
expectedNetworkPoliciesBackedUp: true,
},
{
name: "list all namespaces when cluster-scoped, even with namespace includes",
resourceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
resourceGroup: "certificates.k8s.io",
resourceVersion: "v1beta1",
resourceGV: "certificates.k8s.io/v1beta1",
resourceName: "certificatesigningrequests",
resourceNamespaced: false,
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("a"),
expectedListedNamespaces: []string{""},
labelSelector: "a=b",
lists: []string{
`{
"apiVersion": "certifiaces.k8s.io/v1beta1",
"kind": "CertificateSigningRequestList",
"items": [
{
"metadata": {
"name": "1",
"labels": {
"a": "b"
}
}
}
]
}`,
},
},
{
name: "use a custom action",
resourceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
resourceGroup: "certificates.k8s.io",
resourceVersion: "v1beta1",
resourceGV: "certificates.k8s.io/v1beta1",
resourceName: "certificatesigningrequests",
resourceNamespaced: false,
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("a"),
expectedListedNamespaces: []string{""},
labelSelector: "a=b",
lists: []string{
`{
"apiVersion": "certificates.k8s.io/v1beta1",
"kind": "CertificateSigningRequestList",
"items": [
{
"metadata": {
"name": "1",
"labels": {
"a": "b"
}
}
}
]
}`,
},
actions: map[string]Action{
"certificatesigningrequests": &fakeAction{},
"other": &fakeAction{},
},
expectedActionIDs: map[string][]string{
"certificatesigningrequests": {"1"},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var labelSelector *metav1.LabelSelector
if test.labelSelector != "" {
s, err := metav1.ParseToLabelSelector(test.labelSelector)
require.NoError(t, err)
labelSelector = s
}
log, _ := testlogger.NewNullLogger()
ctx := &backupContext{
backup: &v1.Backup{
Spec: v1.BackupSpec{
LabelSelector: labelSelector,
},
},
resourceIncludesExcludes: test.resourceIncludesExcludes,
namespaceIncludesExcludes: test.namespaceIncludesExcludes,
deploymentsBackedUp: test.deploymentsBackedUp,
networkPoliciesBackedUp: test.networkPoliciesBackedUp,
logger: log,
}
group := &metav1.APIResourceList{
GroupVersion: test.resourceGV,
}
resource := metav1.APIResource{Name: test.resourceName, Namespaced: test.resourceNamespaced}
itemBackupper := &mockItemBackupper{}
var actualActionIDs map[string][]string
dynamicFactory := &arktest.FakeDynamicFactory{}
gvr := schema.GroupVersionResource{Group: test.resourceGroup, Version: test.resourceVersion}
gr := schema.GroupResource{Group: test.resourceGroup, Resource: test.resourceName}
for i, namespace := range test.expectedListedNamespaces {
obj := toRuntimeObject(t, test.lists[i])
client := &arktest.FakeDynamicClient{}
client.On("List", metav1.ListOptions{LabelSelector: test.labelSelector}).Return(obj, nil)
dynamicFactory.On("ClientForGroupVersionResource", gvr, resource, namespace).Return(client, nil)
action := test.actions[test.resourceName]
list, err := meta.ExtractList(obj)
require.NoError(t, err)
for i := range list {
item := list[i].(*unstructured.Unstructured)
itemBackupper.On("backupItem", ctx, item, gr).Return(nil)
if action != nil {
a, err := meta.Accessor(item)
require.NoError(t, err)
ns := a.GetNamespace()
name := a.GetName()
id := ns
if id != "" {
id += "/"
}
id += name
if actualActionIDs == nil {
actualActionIDs = make(map[string][]string)
}
actualActionIDs[test.resourceName] = append(actualActionIDs[test.resourceName], id)
}
}
}
resources := map[schema.GroupVersionResource]schema.GroupVersionResource{
schema.GroupVersionResource{Resource: "certificatesigningrequests"}: schema.GroupVersionResource{Group: "certificates.k8s.io", Version: "v1beta1", Resource: "certificatesigningrequests"},
schema.GroupVersionResource{Resource: "other"}: schema.GroupVersionResource{Group: "somegroup", Version: "someversion", Resource: "otherthings"},
}
discoveryHelper := arktest.NewFakeDiscoveryHelper(false, resources)
podCommandExecutor := &arktest.PodCommandExecutor{}
defer podCommandExecutor.AssertExpectations(t)
kb, err := NewKubernetesBackupper(discoveryHelper, dynamicFactory, test.actions, podCommandExecutor)
require.NoError(t, err)
backupper := kb.(*kubernetesBackupper)
backupper.itemBackupper = itemBackupper
err = backupper.backupResource(ctx, group, resource)
assert.Equal(t, test.expectedDeploymentsBackedUp, ctx.deploymentsBackedUp)
assert.Equal(t, test.expectedNetworkPoliciesBackedUp, ctx.networkPoliciesBackedUp)
assert.Equal(t, test.expectedActionIDs, actualActionIDs)
})
}
}
*/