From 0735ee7218166935726f0f1108511a6160475af0 Mon Sep 17 00:00:00 2001 From: Steve Kriss Date: Thu, 20 Jun 2019 14:28:11 -0600 Subject: [PATCH] extract shared API test helpers to pkg/test Signed-off-by: Steve Kriss --- pkg/backup/backup_new_test.go | 868 ++++++++++++++-------------------- pkg/test/api_server.go | 57 +++ pkg/test/discovery_client.go | 23 +- pkg/test/resources.go | 168 +++++++ 4 files changed, 599 insertions(+), 517 deletions(-) create mode 100644 pkg/test/api_server.go create mode 100644 pkg/test/resources.go diff --git a/pkg/backup/backup_new_test.go b/pkg/backup/backup_new_test.go index ca5912910..55c1cfb4a 100644 --- a/pkg/backup/backup_new_test.go +++ b/pkg/backup/backup_new_test.go @@ -32,21 +32,15 @@ import ( "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - discoveryfake "k8s.io/client-go/discovery/fake" - dynamicfake "k8s.io/client-go/dynamic/fake" - kubefake "k8s.io/client-go/kubernetes/fake" velerov1 "github.com/heptio/velero/pkg/apis/velero/v1" "github.com/heptio/velero/pkg/client" "github.com/heptio/velero/pkg/discovery" - "github.com/heptio/velero/pkg/generated/clientset/versioned/fake" "github.com/heptio/velero/pkg/kuberesource" "github.com/heptio/velero/pkg/plugin/velero" "github.com/heptio/velero/pkg/test" @@ -64,20 +58,20 @@ func TestBackupResourceFiltering(t *testing.T) { tests := []struct { name string backup *velerov1.Backup - apiResources []*apiResource + apiResources []*test.APIResource want []string }{ { name: "no filters backs up everything", backup: defaultBackup().Backup(), - apiResources: []*apiResource{ - pods( - newPod("foo", "bar"), - newPod("zoo", "raz"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("foo", "bar"), + test.NewPod("zoo", "raz"), ), - deployments( - newDeployment("foo", "bar"), - newDeployment("zoo", "raz"), + test.Deployments( + test.NewDeployment("foo", "bar"), + test.NewDeployment("zoo", "raz"), ), }, want: []string{ @@ -92,14 +86,14 @@ func TestBackupResourceFiltering(t *testing.T) { backup: defaultBackup(). IncludedResources("pods"). Backup(), - apiResources: []*apiResource{ - pods( - newPod("foo", "bar"), - newPod("zoo", "raz"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("foo", "bar"), + test.NewPod("zoo", "raz"), ), - deployments( - newDeployment("foo", "bar"), - newDeployment("zoo", "raz"), + test.Deployments( + test.NewDeployment("foo", "bar"), + test.NewDeployment("zoo", "raz"), ), }, want: []string{ @@ -112,14 +106,14 @@ func TestBackupResourceFiltering(t *testing.T) { backup: defaultBackup(). ExcludedResources("deployments"). Backup(), - apiResources: []*apiResource{ - pods( - newPod("foo", "bar"), - newPod("zoo", "raz"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("foo", "bar"), + test.NewPod("zoo", "raz"), ), - deployments( - newDeployment("foo", "bar"), - newDeployment("zoo", "raz"), + test.Deployments( + test.NewDeployment("foo", "bar"), + test.NewDeployment("zoo", "raz"), ), }, want: []string{ @@ -132,14 +126,14 @@ func TestBackupResourceFiltering(t *testing.T) { backup: defaultBackup(). IncludedNamespaces("foo"). Backup(), - apiResources: []*apiResource{ - pods( - newPod("foo", "bar"), - newPod("zoo", "raz"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("foo", "bar"), + test.NewPod("zoo", "raz"), ), - deployments( - newDeployment("foo", "bar"), - newDeployment("zoo", "raz"), + test.Deployments( + test.NewDeployment("foo", "bar"), + test.NewDeployment("zoo", "raz"), ), }, want: []string{ @@ -152,14 +146,14 @@ func TestBackupResourceFiltering(t *testing.T) { backup: defaultBackup(). ExcludedNamespaces("zoo"). Backup(), - apiResources: []*apiResource{ - pods( - newPod("foo", "bar"), - newPod("zoo", "raz"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("foo", "bar"), + test.NewPod("zoo", "raz"), ), - deployments( - newDeployment("foo", "bar"), - newDeployment("zoo", "raz"), + test.Deployments( + test.NewDeployment("foo", "bar"), + test.NewDeployment("zoo", "raz"), ), }, want: []string{ @@ -172,18 +166,18 @@ func TestBackupResourceFiltering(t *testing.T) { backup: defaultBackup(). IncludeClusterResources(false). Backup(), - apiResources: []*apiResource{ - pods( - newPod("foo", "bar"), - newPod("zoo", "raz"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("foo", "bar"), + test.NewPod("zoo", "raz"), ), - deployments( - newDeployment("foo", "bar"), - newDeployment("zoo", "raz"), + test.Deployments( + test.NewDeployment("foo", "bar"), + test.NewDeployment("zoo", "raz"), ), - pvs( - newPV("bar"), - newPV("baz"), + test.PVs( + test.NewPV("bar"), + test.NewPV("baz"), ), }, want: []string{ @@ -198,18 +192,18 @@ func TestBackupResourceFiltering(t *testing.T) { backup: defaultBackup(). LabelSelector(&metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}}). Backup(), - apiResources: []*apiResource{ - pods( - withLabel(newPod("foo", "bar"), "a", "b"), - newPod("zoo", "raz"), + apiResources: []*test.APIResource{ + test.Pods( + withLabel(test.NewPod("foo", "bar"), "a", "b"), + test.NewPod("zoo", "raz"), ), - deployments( - newDeployment("foo", "bar"), - withLabel(newDeployment("zoo", "raz"), "a", "b"), + test.Deployments( + test.NewDeployment("foo", "bar"), + withLabel(test.NewDeployment("zoo", "raz"), "a", "b"), ), - pvs( - withLabel(newPV("bar"), "a", "b"), - withLabel(newPV("baz"), "a", "c"), + test.PVs( + withLabel(test.NewPV("bar"), "a", "b"), + withLabel(test.NewPV("baz"), "a", "c"), ), }, want: []string{ @@ -222,18 +216,18 @@ func TestBackupResourceFiltering(t *testing.T) { name: "resources with velero.io/exclude-from-backup=true label are not included", backup: defaultBackup(). Backup(), - apiResources: []*apiResource{ - pods( - withLabel(newPod("foo", "bar"), "velero.io/exclude-from-backup", "true"), - newPod("zoo", "raz"), + apiResources: []*test.APIResource{ + test.Pods( + withLabel(test.NewPod("foo", "bar"), "velero.io/exclude-from-backup", "true"), + test.NewPod("zoo", "raz"), ), - deployments( - newDeployment("foo", "bar"), - withLabel(newDeployment("zoo", "raz"), "velero.io/exclude-from-backup", "true"), + test.Deployments( + test.NewDeployment("foo", "bar"), + withLabel(test.NewDeployment("zoo", "raz"), "velero.io/exclude-from-backup", "true"), ), - pvs( - withLabel(newPV("bar"), "a", "b"), - withLabel(newPV("baz"), "velero.io/exclude-from-backup", "true"), + test.PVs( + withLabel(test.NewPV("bar"), "a", "b"), + withLabel(test.NewPV("baz"), "velero.io/exclude-from-backup", "true"), ), }, want: []string{ @@ -247,18 +241,18 @@ func TestBackupResourceFiltering(t *testing.T) { backup: defaultBackup(). LabelSelector(&metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}}). Backup(), - apiResources: []*apiResource{ - pods( - withLabel(newPod("foo", "bar"), "velero.io/exclude-from-backup", "true", "a", "b"), - withLabel(newPod("zoo", "raz"), "a", "b"), + apiResources: []*test.APIResource{ + test.Pods( + withLabel(test.NewPod("foo", "bar"), "velero.io/exclude-from-backup", "true", "a", "b"), + withLabel(test.NewPod("zoo", "raz"), "a", "b"), ), - deployments( - newDeployment("foo", "bar"), - withLabel(newDeployment("zoo", "raz"), "velero.io/exclude-from-backup", "true", "a", "b"), + test.Deployments( + test.NewDeployment("foo", "bar"), + withLabel(test.NewDeployment("zoo", "raz"), "velero.io/exclude-from-backup", "true", "a", "b"), ), - pvs( - withLabel(newPV("bar"), "a", "b"), - withLabel(newPV("baz"), "a", "b", "velero.io/exclude-from-backup", "true"), + test.PVs( + withLabel(test.NewPV("bar"), "a", "b"), + withLabel(test.NewPV("baz"), "a", "b", "velero.io/exclude-from-backup", "true"), ), }, want: []string{ @@ -270,18 +264,18 @@ func TestBackupResourceFiltering(t *testing.T) { name: "resources with velero.io/exclude-from-backup label specified but not 'true' are included", backup: defaultBackup(). Backup(), - apiResources: []*apiResource{ - pods( - withLabel(newPod("foo", "bar"), "velero.io/exclude-from-backup", "false"), - newPod("zoo", "raz"), + apiResources: []*test.APIResource{ + test.Pods( + withLabel(test.NewPod("foo", "bar"), "velero.io/exclude-from-backup", "false"), + test.NewPod("zoo", "raz"), ), - deployments( - newDeployment("foo", "bar"), - withLabel(newDeployment("zoo", "raz"), "velero.io/exclude-from-backup", "1"), + test.Deployments( + test.NewDeployment("foo", "bar"), + withLabel(test.NewDeployment("zoo", "raz"), "velero.io/exclude-from-backup", "1"), ), - pvs( - withLabel(newPV("bar"), "a", "b"), - withLabel(newPV("baz"), "velero.io/exclude-from-backup", ""), + test.PVs( + withLabel(test.NewPV("bar"), "a", "b"), + withLabel(test.NewPV("baz"), "velero.io/exclude-from-backup", ""), ), }, want: []string{ @@ -299,15 +293,15 @@ func TestBackupResourceFiltering(t *testing.T) { IncludedNamespaces("ns-1", "ns-2"). IncludeClusterResources(true). Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), - newPod("ns-2", "pod-1"), - newPod("ns-3", "pod-1"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), + test.NewPod("ns-2", "pod-1"), + test.NewPod("ns-3", "pod-1"), ), - pvs( - newPV("pv-1"), - newPV("pv-2"), + test.PVs( + test.NewPV("pv-1"), + test.NewPV("pv-2"), ), }, want: []string{ @@ -323,15 +317,15 @@ func TestBackupResourceFiltering(t *testing.T) { IncludedNamespaces("ns-1", "ns-2"). IncludeClusterResources(false). Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), - newPod("ns-2", "pod-1"), - newPod("ns-3", "pod-1"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), + test.NewPod("ns-2", "pod-1"), + test.NewPod("ns-3", "pod-1"), ), - pvs( - newPV("pv-1"), - newPV("pv-2"), + test.PVs( + test.NewPV("pv-1"), + test.NewPV("pv-2"), ), }, want: []string{ @@ -344,15 +338,15 @@ func TestBackupResourceFiltering(t *testing.T) { backup: defaultBackup(). IncludedNamespaces("ns-1", "ns-2"). Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), - newPod("ns-2", "pod-1"), - newPod("ns-3", "pod-1"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), + test.NewPod("ns-2", "pod-1"), + test.NewPod("ns-3", "pod-1"), ), - pvs( - newPV("pv-1"), - newPV("pv-2"), + test.PVs( + test.NewPV("pv-1"), + test.NewPV("pv-2"), ), }, want: []string{ @@ -365,15 +359,15 @@ func TestBackupResourceFiltering(t *testing.T) { backup: defaultBackup(). IncludeClusterResources(true). Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), - newPod("ns-2", "pod-1"), - newPod("ns-3", "pod-1"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), + test.NewPod("ns-2", "pod-1"), + test.NewPod("ns-3", "pod-1"), ), - pvs( - newPV("pv-1"), - newPV("pv-2"), + test.PVs( + test.NewPV("pv-1"), + test.NewPV("pv-2"), ), }, want: []string{ @@ -389,15 +383,15 @@ func TestBackupResourceFiltering(t *testing.T) { backup: defaultBackup(). IncludeClusterResources(false). Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), - newPod("ns-2", "pod-1"), - newPod("ns-3", "pod-1"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), + test.NewPod("ns-2", "pod-1"), + test.NewPod("ns-3", "pod-1"), ), - pvs( - newPV("pv-1"), - newPV("pv-2"), + test.PVs( + test.NewPV("pv-1"), + test.NewPV("pv-2"), ), }, want: []string{ @@ -410,15 +404,15 @@ func TestBackupResourceFiltering(t *testing.T) { name: "should include cluster-scoped resources if backing up all namespaces and IncludeClusterResources=nil", backup: defaultBackup(). Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), - newPod("ns-2", "pod-1"), - newPod("ns-3", "pod-1"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), + test.NewPod("ns-2", "pod-1"), + test.NewPod("ns-3", "pod-1"), ), - pvs( - newPV("pv-1"), - newPV("pv-2"), + test.PVs( + test.NewPV("pv-1"), + test.NewPV("pv-2"), ), }, want: []string{ @@ -434,14 +428,14 @@ func TestBackupResourceFiltering(t *testing.T) { backup: defaultBackup(). IncludedResources("*", "pods"). Backup(), - apiResources: []*apiResource{ - pods( - newPod("foo", "bar"), - newPod("zoo", "raz"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("foo", "bar"), + test.NewPod("zoo", "raz"), ), - deployments( - newDeployment("foo", "bar"), - newDeployment("zoo", "raz"), + test.Deployments( + test.NewDeployment("foo", "bar"), + test.NewDeployment("zoo", "raz"), ), }, want: []string{ @@ -456,14 +450,14 @@ func TestBackupResourceFiltering(t *testing.T) { backup: defaultBackup(). ExcludedResources("*"). Backup(), - apiResources: []*apiResource{ - pods( - newPod("foo", "bar"), - newPod("zoo", "raz"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("foo", "bar"), + test.NewPod("zoo", "raz"), ), - deployments( - newDeployment("foo", "bar"), - newDeployment("zoo", "raz"), + test.Deployments( + test.NewDeployment("foo", "bar"), + test.NewDeployment("zoo", "raz"), ), }, want: []string{ @@ -478,14 +472,14 @@ func TestBackupResourceFiltering(t *testing.T) { backup: defaultBackup(). IncludedResources("pods", "unresolvable"). Backup(), - apiResources: []*apiResource{ - pods( - newPod("foo", "bar"), - newPod("zoo", "raz"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("foo", "bar"), + test.NewPod("zoo", "raz"), ), - deployments( - newDeployment("foo", "bar"), - newDeployment("zoo", "raz"), + test.Deployments( + test.NewDeployment("foo", "bar"), + test.NewDeployment("zoo", "raz"), ), }, want: []string{ @@ -498,14 +492,14 @@ func TestBackupResourceFiltering(t *testing.T) { backup: defaultBackup(). ExcludedResources("deployments", "unresolvable"). Backup(), - apiResources: []*apiResource{ - pods( - newPod("foo", "bar"), - newPod("zoo", "raz"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("foo", "bar"), + test.NewPod("zoo", "raz"), ), - deployments( - newDeployment("foo", "bar"), - newDeployment("zoo", "raz"), + test.Deployments( + test.NewDeployment("foo", "bar"), + test.NewDeployment("zoo", "raz"), ), }, want: []string{ @@ -516,9 +510,9 @@ func TestBackupResourceFiltering(t *testing.T) { { name: "terminating resources are not backed up", backup: defaultBackup().Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "ns-2", Name: "pod-2", DeletionTimestamp: &metav1.Time{Time: time.Now()}}}, ), }, @@ -537,7 +531,7 @@ func TestBackupResourceFiltering(t *testing.T) { ) for _, resource := range tc.apiResources { - h.addItems(t, resource.group, resource.version, resource.name, resource.shortName, resource.namespaced, resource.items...) + h.addItems(t, resource) } h.backupper.Backup(h.log, req, backupFile, nil, nil) @@ -555,16 +549,16 @@ func TestBackupResourceCohabitation(t *testing.T) { tests := []struct { name string backup *velerov1.Backup - apiResources []*apiResource + apiResources []*test.APIResource want []string }{ { name: "when deployments exist only in extensions, they're backed up", backup: defaultBackup().Backup(), - apiResources: []*apiResource{ - extensionsDeployments( - newDeployment("foo", "bar"), - newDeployment("zoo", "raz"), + apiResources: []*test.APIResource{ + test.ExtensionsDeployments( + test.NewDeployment("foo", "bar"), + test.NewDeployment("zoo", "raz"), ), }, want: []string{ @@ -575,14 +569,14 @@ func TestBackupResourceCohabitation(t *testing.T) { { name: "when deployments exist in both apps and extensions, only apps/deployments are backed up", backup: defaultBackup().Backup(), - apiResources: []*apiResource{ - extensionsDeployments( - newDeployment("foo", "bar"), - newDeployment("zoo", "raz"), + apiResources: []*test.APIResource{ + test.ExtensionsDeployments( + test.NewDeployment("foo", "bar"), + test.NewDeployment("zoo", "raz"), ), - deployments( - newDeployment("foo", "bar"), - newDeployment("zoo", "raz"), + test.Deployments( + test.NewDeployment("foo", "bar"), + test.NewDeployment("zoo", "raz"), ), }, want: []string{ @@ -601,7 +595,7 @@ func TestBackupResourceCohabitation(t *testing.T) { ) for _, resource := range tc.apiResources { - h.addItems(t, resource.group, resource.version, resource.name, resource.shortName, resource.namespaced, resource.items...) + h.addItems(t, resource) } h.backupper.Backup(h.log, req, backupFile, nil, nil) @@ -624,8 +618,8 @@ func TestBackupUsesNewCohabitatingResourcesForEachBackup(t *testing.T) { } backup1File := bytes.NewBuffer([]byte{}) - h.addItems(t, "apps", "v1", "deployments", "deploys", true, newDeployment("ns-1", "deploy-1")) - h.addItems(t, "extensions", "v1", "deployments", "deploys", true, newDeployment("ns-1", "deploy-1")) + h.addItems(t, test.Deployments(test.NewDeployment("ns-1", "deploy-1"))) + h.addItems(t, test.ExtensionsDeployments(test.NewDeployment("ns-1", "deploy-1"))) h.backupper.Backup(h.log, backup1, backup1File, nil, nil) @@ -649,29 +643,29 @@ func TestBackupResourceOrdering(t *testing.T) { tests := []struct { name string backup *velerov1.Backup - apiResources []*apiResource + apiResources []*test.APIResource }{ { name: "core API group: pods come before pvcs, pvcs come before pvs, pvs come before anything else", backup: defaultBackup(). SnapshotVolumes(false). Backup(), - apiResources: []*apiResource{ - pods( - newPod("foo", "bar"), - newPod("zoo", "raz"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("foo", "bar"), + test.NewPod("zoo", "raz"), ), - pvcs( - newPVC("foo", "bar"), - newPVC("zoo", "raz"), + test.PVCs( + test.NewPVC("foo", "bar"), + test.NewPVC("zoo", "raz"), ), - pvs( - newPV("bar"), - newPV("baz"), + test.PVs( + test.NewPV("bar"), + test.NewPV("baz"), ), - secrets( - newSecret("foo", "bar"), - newSecret("zoo", "raz"), + test.Secrets( + test.NewSecret("foo", "bar"), + test.NewSecret("zoo", "raz"), ), }, }, @@ -686,7 +680,7 @@ func TestBackupResourceOrdering(t *testing.T) { ) for _, resource := range tc.apiResources { - h.addItems(t, resource.group, resource.version, resource.name, resource.shortName, resource.namespaced, resource.items...) + h.addItems(t, resource) } h.backupper.Backup(h.log, req, backupFile, nil, nil) @@ -749,7 +743,7 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) { tests := []struct { name string backup *velerov1.Backup - apiResources []*apiResource + apiResources []*test.APIResource // actions is a map from a recordResourcesAction (which will record the items it was called for) // to a slice of expected items, formatted as {namespace}/{name}. @@ -759,14 +753,14 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) { name: "single action with no selector runs for all items", backup: defaultBackup(). Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), - newPod("ns-2", "pod-2"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), + test.NewPod("ns-2", "pod-2"), ), - pvs( - newPV("pv-1"), - newPV("pv-2"), + test.PVs( + test.NewPV("pv-1"), + test.NewPV("pv-2"), ), }, actions: map[*recordResourcesAction][]string{ @@ -777,14 +771,14 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) { name: "single action with a resource selector for namespaced resources runs only for matching resources", backup: defaultBackup(). Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), - newPod("ns-2", "pod-2"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), + test.NewPod("ns-2", "pod-2"), ), - pvs( - newPV("pv-1"), - newPV("pv-2"), + test.PVs( + test.NewPV("pv-1"), + test.NewPV("pv-2"), ), }, actions: map[*recordResourcesAction][]string{ @@ -795,14 +789,14 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) { name: "single action with a resource selector for cluster-scoped resources runs only for matching resources", backup: defaultBackup(). Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), - newPod("ns-2", "pod-2"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), + test.NewPod("ns-2", "pod-2"), ), - pvs( - newPV("pv-1"), - newPV("pv-2"), + test.PVs( + test.NewPV("pv-1"), + test.NewPV("pv-2"), ), }, actions: map[*recordResourcesAction][]string{ @@ -814,18 +808,18 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) { name: "single action with a namespace selector runs for resources in that namespace plus cluster-scoped resources", backup: defaultBackup(). Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), - newPod("ns-2", "pod-2"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), + test.NewPod("ns-2", "pod-2"), ), - pvcs( - newPVC("ns-1", "pvc-1"), - newPVC("ns-2", "pvc-2"), + test.PVCs( + test.NewPVC("ns-1", "pvc-1"), + test.NewPVC("ns-2", "pvc-2"), ), - pvs( - newPV("pv-1"), - newPV("pv-2"), + test.PVs( + test.NewPV("pv-1"), + test.NewPV("pv-2"), ), }, actions: map[*recordResourcesAction][]string{ @@ -836,14 +830,14 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) { name: "single action with a resource and namespace selector runs only for matching resources", backup: defaultBackup(). Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), - newPod("ns-2", "pod-2"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), + test.NewPod("ns-2", "pod-2"), ), - pvs( - newPV("pv-1"), - newPV("pv-2"), + test.PVs( + test.NewPV("pv-1"), + test.NewPV("pv-2"), ), }, actions: map[*recordResourcesAction][]string{ @@ -854,14 +848,14 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) { name: "multiple actions, each with a different resource selector using short name, run for matching resources", backup: defaultBackup(). Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), - newPod("ns-2", "pod-2"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), + test.NewPod("ns-2", "pod-2"), ), - pvs( - newPV("pv-1"), - newPV("pv-2"), + test.PVs( + test.NewPV("pv-1"), + test.NewPV("pv-2"), ), }, actions: map[*recordResourcesAction][]string{ @@ -873,16 +867,16 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) { name: "actions with selectors that don't match anything don't run for any resources", backup: defaultBackup(). Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), ), - pvcs( - newPVC("ns-2", "pvc-2"), + test.PVCs( + test.NewPVC("ns-2", "pvc-2"), ), - pvs( - newPV("pv-1"), - newPV("pv-2"), + test.PVs( + test.NewPV("pv-1"), + test.NewPV("pv-2"), ), }, actions: map[*recordResourcesAction][]string{ @@ -901,7 +895,7 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) { ) for _, resource := range tc.apiResources { - h.addItems(t, resource.group, resource.version, resource.name, resource.shortName, resource.namespaced, resource.items...) + h.addItems(t, resource) } actions := []velero.BackupItemAction{} @@ -929,21 +923,21 @@ func TestBackupWithInvalidActions(t *testing.T) { tests := []struct { name string backup *velerov1.Backup - apiResources []*apiResource + apiResources []*test.APIResource actions []velero.BackupItemAction }{ { name: "action with invalid label selector results in an error", backup: defaultBackup(). Backup(), - apiResources: []*apiResource{ - pods( - newPod("foo", "bar"), - newPod("zoo", "raz"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("foo", "bar"), + test.NewPod("zoo", "raz"), ), - pvs( - newPV("bar"), - newPV("baz"), + test.PVs( + test.NewPV("bar"), + test.NewPV("baz"), ), }, actions: []velero.BackupItemAction{ @@ -954,14 +948,14 @@ func TestBackupWithInvalidActions(t *testing.T) { name: "action returning an error from AppliesTo results in an error", backup: defaultBackup(). Backup(), - apiResources: []*apiResource{ - pods( - newPod("foo", "bar"), - newPod("zoo", "raz"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("foo", "bar"), + test.NewPod("zoo", "raz"), ), - pvs( - newPV("bar"), - newPV("baz"), + test.PVs( + test.NewPV("bar"), + test.NewPV("baz"), ), }, actions: []velero.BackupItemAction{ @@ -979,7 +973,7 @@ func TestBackupWithInvalidActions(t *testing.T) { ) for _, resource := range tc.apiResources { - h.addItems(t, resource.group, resource.version, resource.name, resource.shortName, resource.namespaced, resource.items...) + h.addItems(t, resource) } assert.Error(t, h.backupper.Backup(h.log, req, backupFile, tc.actions, nil)) @@ -1025,16 +1019,16 @@ func TestBackupActionModifications(t *testing.T) { tests := []struct { name string backup *velerov1.Backup - apiResources []*apiResource + apiResources []*test.APIResource actions []velero.BackupItemAction want map[string]unstructuredObject }{ { name: "action that adds a label to item gets persisted", backup: defaultBackup().Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), ), }, actions: []velero.BackupItemAction{ @@ -1043,15 +1037,15 @@ func TestBackupActionModifications(t *testing.T) { }), }, want: map[string]unstructuredObject{ - "resources/pods/namespaces/ns-1/pod-1.json": toUnstructuredOrFail(t, withLabel(newPod("ns-1", "pod-1"), "updated", "true")), + "resources/pods/namespaces/ns-1/pod-1.json": toUnstructuredOrFail(t, withLabel(test.NewPod("ns-1", "pod-1"), "updated", "true")), }, }, { name: "action that removes labels from item gets persisted", backup: defaultBackup().Backup(), - apiResources: []*apiResource{ - pods( - withLabel(newPod("ns-1", "pod-1"), "should-be-removed", "true"), + apiResources: []*test.APIResource{ + test.Pods( + withLabel(test.NewPod("ns-1", "pod-1"), "should-be-removed", "true"), ), }, actions: []velero.BackupItemAction{ @@ -1060,15 +1054,15 @@ func TestBackupActionModifications(t *testing.T) { }), }, want: map[string]unstructuredObject{ - "resources/pods/namespaces/ns-1/pod-1.json": toUnstructuredOrFail(t, newPod("ns-1", "pod-1")), + "resources/pods/namespaces/ns-1/pod-1.json": toUnstructuredOrFail(t, test.NewPod("ns-1", "pod-1")), }, }, { name: "action that sets a spec field on item gets persisted", backup: defaultBackup().Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), ), }, actions: []velero.BackupItemAction{ @@ -1077,16 +1071,16 @@ func TestBackupActionModifications(t *testing.T) { }), }, want: map[string]unstructuredObject{ - "resources/pods/namespaces/ns-1/pod-1.json": toUnstructuredOrFail(t, &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "ns-1", Name: "pod-1"}, Spec: corev1.PodSpec{NodeName: "foo"}}), + "resources/pods/namespaces/ns-1/pod-1.json": toUnstructuredOrFail(t, &corev1.Pod{TypeMeta: metav1.TypeMeta{Kind: "Pod", APIVersion: "v1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "ns-1", Name: "pod-1"}, Spec: corev1.PodSpec{NodeName: "foo"}}), }, }, { name: "modifications to name and namespace in an action are persisted in JSON and in filename", backup: defaultBackup(). Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), ), }, actions: []velero.BackupItemAction{ @@ -1096,7 +1090,7 @@ func TestBackupActionModifications(t *testing.T) { }), }, want: map[string]unstructuredObject{ - "resources/pods/namespaces/ns-1-updated/pod-1-updated.json": toUnstructuredOrFail(t, newPod("ns-1-updated", "pod-1-updated")), + "resources/pods/namespaces/ns-1-updated/pod-1-updated.json": toUnstructuredOrFail(t, test.NewPod("ns-1-updated", "pod-1-updated")), }, }, } @@ -1110,7 +1104,7 @@ func TestBackupActionModifications(t *testing.T) { ) for _, resource := range tc.apiResources { - h.addItems(t, resource.group, resource.version, resource.name, resource.shortName, resource.namespaced, resource.items...) + h.addItems(t, resource) } err := h.backupper.Backup(h.log, req, backupFile, tc.actions, nil) @@ -1130,18 +1124,18 @@ func TestBackupActionAdditionalItems(t *testing.T) { tests := []struct { name string backup *velerov1.Backup - apiResources []*apiResource + apiResources []*test.APIResource actions []velero.BackupItemAction want []string }{ { name: "additional items that are already being backed up are not backed up twice", backup: defaultBackup().Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), - newPod("ns-2", "pod-2"), - newPod("ns-3", "pod-3"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), + test.NewPod("ns-2", "pod-2"), + test.NewPod("ns-3", "pod-3"), ), }, actions: []velero.BackupItemAction{ @@ -1166,11 +1160,11 @@ func TestBackupActionAdditionalItems(t *testing.T) { { name: "when using a backup namespace filter, additional items that are in a non-included namespace are not backed up", backup: defaultBackup().IncludedNamespaces("ns-1").Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), - newPod("ns-2", "pod-2"), - newPod("ns-3", "pod-3"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), + test.NewPod("ns-2", "pod-2"), + test.NewPod("ns-3", "pod-3"), ), }, actions: []velero.BackupItemAction{ @@ -1192,14 +1186,14 @@ func TestBackupActionAdditionalItems(t *testing.T) { { name: "when using a backup namespace filter, additional items that are cluster-scoped are backed up", backup: defaultBackup().IncludedNamespaces("ns-1").Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), - newPod("ns-2", "pod-2"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), + test.NewPod("ns-2", "pod-2"), ), - pvs( - newPV("pv-1"), - newPV("pv-2"), + test.PVs( + test.NewPV("pv-1"), + test.NewPV("pv-2"), ), }, actions: []velero.BackupItemAction{ @@ -1223,13 +1217,13 @@ func TestBackupActionAdditionalItems(t *testing.T) { { name: "when using a backup resource filter, additional items that are non-included resources are not backed up", backup: defaultBackup().IncludedResources("pods").Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), ), - pvs( - newPV("pv-1"), - newPV("pv-2"), + test.PVs( + test.NewPV("pv-1"), + test.NewPV("pv-2"), ), }, actions: []velero.BackupItemAction{ @@ -1251,14 +1245,14 @@ func TestBackupActionAdditionalItems(t *testing.T) { { name: "when IncludeClusterResources=false, additional items that are cluster-scoped are not backed up", backup: defaultBackup().IncludeClusterResources(false).Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), - newPod("ns-2", "pod-2"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), + test.NewPod("ns-2", "pod-2"), ), - pvs( - newPV("pv-1"), - newPV("pv-2"), + test.PVs( + test.NewPV("pv-1"), + test.NewPV("pv-2"), ), }, actions: []velero.BackupItemAction{ @@ -1281,11 +1275,11 @@ func TestBackupActionAdditionalItems(t *testing.T) { { name: "if there's an error backing up additional items, the item the action was run for isn't backed up", backup: defaultBackup().Backup(), - apiResources: []*apiResource{ - pods( - newPod("ns-1", "pod-1"), - newPod("ns-2", "pod-2"), - newPod("ns-3", "pod-3"), + apiResources: []*test.APIResource{ + test.Pods( + test.NewPod("ns-1", "pod-1"), + test.NewPod("ns-2", "pod-2"), + test.NewPod("ns-3", "pod-3"), ), }, actions: []velero.BackupItemAction{ @@ -1317,7 +1311,7 @@ func TestBackupActionAdditionalItems(t *testing.T) { ) for _, resource := range tc.apiResources { - h.addItems(t, resource.group, resource.version, resource.name, resource.shortName, resource.namespaced, resource.items...) + h.addItems(t, resource) } err := h.backupper.Backup(h.log, req, backupFile, tc.actions, nil) @@ -1463,7 +1457,7 @@ func TestBackupWithSnapshots(t *testing.T) { name string req *Request vsls []*velerov1.VolumeSnapshotLocation - apiResources []*apiResource + apiResources []*test.APIResource snapshotterGetter volumeSnapshotterGetter want []*volume.Snapshot }{ @@ -1475,9 +1469,9 @@ func TestBackupWithSnapshots(t *testing.T) { newSnapshotLocation("velero", "default", "default"), }, }, - apiResources: []*apiResource{ - pvs( - newPV("pv-1"), + apiResources: []*test.APIResource{ + test.PVs( + test.NewPV("pv-1"), ), }, snapshotterGetter: map[string]velero.VolumeSnapshotter{ @@ -1508,9 +1502,9 @@ func TestBackupWithSnapshots(t *testing.T) { newSnapshotLocation("velero", "default", "default"), }, }, - apiResources: []*apiResource{ - pvs( - withLabel(newPV("pv-1"), "failure-domain.beta.kubernetes.io/zone", "zone-1"), + apiResources: []*test.APIResource{ + test.PVs( + withLabel(test.NewPV("pv-1"), "failure-domain.beta.kubernetes.io/zone", "zone-1"), ), }, snapshotterGetter: map[string]velero.VolumeSnapshotter{ @@ -1542,9 +1536,9 @@ func TestBackupWithSnapshots(t *testing.T) { newSnapshotLocation("velero", "default", "default"), }, }, - apiResources: []*apiResource{ - pvs( - newPV("pv-1"), + apiResources: []*test.APIResource{ + test.PVs( + test.NewPV("pv-1"), ), }, snapshotterGetter: map[string]velero.VolumeSnapshotter{ @@ -1574,9 +1568,9 @@ func TestBackupWithSnapshots(t *testing.T) { newSnapshotLocation("velero", "default", "default"), }, }, - apiResources: []*apiResource{ - pvs( - newPV("pv-1"), + apiResources: []*test.APIResource{ + test.PVs( + test.NewPV("pv-1"), ), }, snapshotterGetter: map[string]velero.VolumeSnapshotter{ @@ -1589,9 +1583,9 @@ func TestBackupWithSnapshots(t *testing.T) { req: &Request{ Backup: defaultBackup().Backup(), }, - apiResources: []*apiResource{ - pvs( - newPV("pv-1"), + apiResources: []*test.APIResource{ + test.PVs( + test.NewPV("pv-1"), ), }, snapshotterGetter: map[string]velero.VolumeSnapshotter{ @@ -1607,9 +1601,9 @@ func TestBackupWithSnapshots(t *testing.T) { newSnapshotLocation("velero", "default", "default"), }, }, - apiResources: []*apiResource{ - pvs( - newPV("pv-1"), + apiResources: []*test.APIResource{ + test.PVs( + test.NewPV("pv-1"), ), }, snapshotterGetter: map[string]velero.VolumeSnapshotter{}, @@ -1623,9 +1617,9 @@ func TestBackupWithSnapshots(t *testing.T) { newSnapshotLocation("velero", "default", "default"), }, }, - apiResources: []*apiResource{ - pvs( - newPV("pv-1"), + apiResources: []*test.APIResource{ + test.PVs( + test.NewPV("pv-1"), ), }, snapshotterGetter: map[string]velero.VolumeSnapshotter{ @@ -1642,10 +1636,10 @@ func TestBackupWithSnapshots(t *testing.T) { newSnapshotLocation("velero", "another", "another"), }, }, - apiResources: []*apiResource{ - pvs( - newPV("pv-1"), - newPV("pv-2"), + apiResources: []*test.APIResource{ + test.PVs( + test.NewPV("pv-1"), + test.NewPV("pv-2"), ), }, snapshotterGetter: map[string]velero.VolumeSnapshotter{ @@ -1693,7 +1687,7 @@ func TestBackupWithSnapshots(t *testing.T) { ) for _, resource := range tc.apiResources { - h.addItems(t, resource.group, resource.version, resource.name, resource.shortName, resource.namespaced, resource.items...) + h.addItems(t, resource) } err := h.backupper.Backup(h.log, tc.req, backupFile, nil, tc.snapshotterGetter) @@ -1723,112 +1717,28 @@ func (a *pluggableAction) AppliesTo() (velero.ResourceSelector, error) { return a.selector, nil } -type apiResource struct { - group string - version string - name string - shortName string - namespaced bool - items []metav1.Object -} - -func pods(items ...metav1.Object) *apiResource { - return &apiResource{ - group: "", - version: "v1", - name: "pods", - shortName: "po", - namespaced: true, - items: items, - } -} - -func pvcs(items ...metav1.Object) *apiResource { - return &apiResource{ - group: "", - version: "v1", - name: "persistentvolumeclaims", - shortName: "pvc", - namespaced: true, - items: items, - } -} - -func secrets(items ...metav1.Object) *apiResource { - return &apiResource{ - group: "", - version: "v1", - name: "secrets", - shortName: "secrets", - namespaced: true, - items: items, - } -} - -func deployments(items ...metav1.Object) *apiResource { - return &apiResource{ - group: "apps", - version: "v1", - name: "deployments", - shortName: "deploy", - namespaced: true, - items: items, - } -} - -func extensionsDeployments(items ...metav1.Object) *apiResource { - return &apiResource{ - group: "extensions", - version: "v1", - name: "deployments", - shortName: "deploy", - namespaced: true, - items: items, - } -} - -func pvs(items ...metav1.Object) *apiResource { - return &apiResource{ - group: "", - version: "v1", - name: "persistentvolumes", - shortName: "pv", - namespaced: false, - items: items, - } -} - type harness struct { - veleroClient *fake.Clientset - kubeClient *kubefake.Clientset - dynamicClient *dynamicfake.FakeDynamicClient - discoveryClient *test.DiscoveryClient - backupper *kubernetesBackupper - log logrus.FieldLogger + *test.APIServer + backupper *kubernetesBackupper + log logrus.FieldLogger } -func (h *harness) addItems(t *testing.T, group, version, resource, shortName string, namespaced bool, items ...metav1.Object) { +func (h *harness) addItems(t *testing.T, resource *test.APIResource) { t.Helper() - h.discoveryClient.WithResource(group, version, resource, namespaced, shortName) + h.DiscoveryClient.WithAPIResource(resource) require.NoError(t, h.backupper.discoveryHelper.Refresh()) - gvr := schema.GroupVersionResource{ - Group: group, - Version: version, - Resource: resource, - } - - for _, item := range items { + for _, item := range resource.Items { obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(item) require.NoError(t, err) unstructuredObj := &unstructured.Unstructured{Object: obj} - if namespaced { - _, err = h.dynamicClient.Resource(gvr).Namespace(item.GetNamespace()).Create(unstructuredObj, metav1.CreateOptions{}) + if resource.Namespaced { + _, err = h.DynamicClient.Resource(resource.GVR()).Namespace(item.GetNamespace()).Create(unstructuredObj, metav1.CreateOptions{}) } else { - _, err = h.dynamicClient.Resource(gvr).Create(unstructuredObj, metav1.CreateOptions{}) + _, err = h.DynamicClient.Resource(resource.GVR()).Create(unstructuredObj, metav1.CreateOptions{}) } require.NoError(t, err) } @@ -1837,26 +1747,16 @@ func (h *harness) addItems(t *testing.T, group, version, resource, shortName str func newHarness(t *testing.T) *harness { t.Helper() - // API server fakes - var ( - veleroClient = fake.NewSimpleClientset() - kubeClient = kubefake.NewSimpleClientset() - dynamicClient = dynamicfake.NewSimpleDynamicClient(runtime.NewScheme()) - discoveryClient = &test.DiscoveryClient{FakeDiscovery: kubeClient.Discovery().(*discoveryfake.FakeDiscovery)} - ) - + apiServer := test.NewAPIServer(t) log := logrus.StandardLogger() - discoveryHelper, err := discovery.NewHelper(discoveryClient, log) + discoveryHelper, err := discovery.NewHelper(apiServer.DiscoveryClient, log) require.NoError(t, err) return &harness{ - veleroClient: veleroClient, - kubeClient: kubeClient, - dynamicClient: dynamicClient, - discoveryClient: discoveryClient, + APIServer: apiServer, backupper: &kubernetesBackupper{ - dynamicFactory: client.NewDynamicFactory(dynamicClient), + dynamicFactory: client.NewDynamicFactory(apiServer.DynamicClient), discoveryHelper: discoveryHelper, groupBackupperFactory: new(defaultGroupBackupperFactory), @@ -1885,50 +1785,6 @@ func withLabel(obj metav1.Object, labelPairs ...string) metav1.Object { return obj } -func newPod(ns, name string) *corev1.Pod { - return &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: ns, - Name: name, - }, - } -} - -func newPVC(ns, name string) *corev1.PersistentVolumeClaim { - return &corev1.PersistentVolumeClaim{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: ns, - Name: name, - }, - } -} - -func newSecret(ns, name string) *corev1.Secret { - return &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: ns, - Name: name, - }, - } -} - -func newDeployment(ns, name string) *appsv1.Deployment { - return &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: ns, - Name: name, - }, - } -} - -func newPV(name string) *corev1.PersistentVolume { - return &corev1.PersistentVolume{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - } -} - func newSnapshotLocation(ns, name, provider string) *velerov1.VolumeSnapshotLocation { return &velerov1.VolumeSnapshotLocation{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/test/api_server.go b/pkg/test/api_server.go new file mode 100644 index 000000000..90c3a483b --- /dev/null +++ b/pkg/test/api_server.go @@ -0,0 +1,57 @@ +/* +Copyright 2019 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. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package test + +import ( + "testing" + + "k8s.io/apimachinery/pkg/runtime" + discoveryfake "k8s.io/client-go/discovery/fake" + dynamicfake "k8s.io/client-go/dynamic/fake" + kubefake "k8s.io/client-go/kubernetes/fake" + + "github.com/heptio/velero/pkg/generated/clientset/versioned/fake" +) + +// APIServer contains in-memory fakes for all of the relevant +// Kubernetes API server clients. +type APIServer struct { + VeleroClient *fake.Clientset + KubeClient *kubefake.Clientset + DynamicClient *dynamicfake.FakeDynamicClient + DiscoveryClient *DiscoveryClient +} + +// NewAPIServer constructs an APIServer with all of its clients +// initialized. +func NewAPIServer(t *testing.T) *APIServer { + t.Helper() + + var ( + veleroClient = fake.NewSimpleClientset() + kubeClient = kubefake.NewSimpleClientset() + dynamicClient = dynamicfake.NewSimpleDynamicClient(runtime.NewScheme()) + discoveryClient = &DiscoveryClient{FakeDiscovery: kubeClient.Discovery().(*discoveryfake.FakeDiscovery)} + ) + + return &APIServer{ + VeleroClient: veleroClient, + KubeClient: kubeClient, + DynamicClient: dynamicClient, + DiscoveryClient: discoveryClient, + } +} diff --git a/pkg/test/discovery_client.go b/pkg/test/discovery_client.go index 5a5afbfec..fe06af635 100644 --- a/pkg/test/discovery_client.go +++ b/pkg/test/discovery_client.go @@ -38,10 +38,11 @@ func (c *DiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, // TEST HELPERS // -func (c *DiscoveryClient) WithResource(group, version, resource string, namespaced bool, shortNames ...string) *DiscoveryClient { +// WithAPIResource adds the API resource to the discovery client. +func (c *DiscoveryClient) WithAPIResource(resource *APIResource) *DiscoveryClient { gv := metav1.GroupVersion{ - Group: group, - Version: version, + Group: resource.Group, + Version: resource.Version, } var resourceList *metav1.APIResourceList @@ -61,20 +62,20 @@ func (c *DiscoveryClient) WithResource(group, version, resource string, namespac } for _, itm := range resourceList.APIResources { - if itm.Name == resource { + if itm.Name == resource.Name { return c } } resourceList.APIResources = append(resourceList.APIResources, metav1.APIResource{ - Name: resource, - SingularName: strings.TrimSuffix(resource, "s"), - Namespaced: namespaced, - Group: group, - Version: version, - Kind: strings.Title(strings.TrimSuffix(resource, "s")), + Name: resource.Name, + SingularName: strings.TrimSuffix(resource.Name, "s"), + Namespaced: resource.Namespaced, + Group: resource.Group, + Version: resource.Version, + Kind: strings.Title(strings.TrimSuffix(resource.Name, "s")), Verbs: metav1.Verbs([]string{"list", "create", "get", "delete"}), - ShortNames: shortNames, + ShortNames: []string{resource.ShortName}, }) return c diff --git a/pkg/test/resources.go b/pkg/test/resources.go new file mode 100644 index 000000000..f40fc2c5c --- /dev/null +++ b/pkg/test/resources.go @@ -0,0 +1,168 @@ +/* +Copyright 2019 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. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package test + +import ( + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// APIResource stores information about a specific Kubernetes API +// resource. +type APIResource struct { + Group string + Version string + Name string + ShortName string + Namespaced bool + Items []metav1.Object +} + +// GVR returns a GroupVersionResource representing the resource. +func (r *APIResource) GVR() schema.GroupVersionResource { + return schema.GroupVersionResource{ + Group: r.Group, + Version: r.Version, + Resource: r.Name, + } +} + +// Pods returns an APIResource describing core/v1's Pods. +func Pods(items ...metav1.Object) *APIResource { + return &APIResource{ + Group: "", + Version: "v1", + Name: "pods", + ShortName: "po", + Namespaced: true, + Items: items, + } +} + +func PVCs(items ...metav1.Object) *APIResource { + return &APIResource{ + Group: "", + Version: "v1", + Name: "persistentvolumeclaims", + ShortName: "pvc", + Namespaced: true, + Items: items, + } +} + +func PVs(items ...metav1.Object) *APIResource { + return &APIResource{ + Group: "", + Version: "v1", + Name: "persistentvolumes", + ShortName: "pv", + Namespaced: false, + Items: items, + } +} + +func Secrets(items ...metav1.Object) *APIResource { + return &APIResource{ + Group: "", + Version: "v1", + Name: "secrets", + ShortName: "secrets", + Namespaced: true, + Items: items, + } +} + +func Deployments(items ...metav1.Object) *APIResource { + return &APIResource{ + Group: "apps", + Version: "v1", + Name: "deployments", + ShortName: "deploy", + Namespaced: true, + Items: items, + } +} + +func ExtensionsDeployments(items ...metav1.Object) *APIResource { + return &APIResource{ + Group: "extensions", + Version: "v1", + Name: "deployments", + ShortName: "deploy", + Namespaced: true, + Items: items, + } +} + +func NewPod(ns, name string) *corev1.Pod { + return &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: objectMeta(ns, name), + } +} + +func NewPVC(ns, name string) *corev1.PersistentVolumeClaim { + return &corev1.PersistentVolumeClaim{ + TypeMeta: metav1.TypeMeta{ + Kind: "PersistentVolumeClaim", + APIVersion: "v1", + }, + ObjectMeta: objectMeta(ns, name), + } +} + +func NewPV(name string) *corev1.PersistentVolume { + return &corev1.PersistentVolume{ + TypeMeta: metav1.TypeMeta{ + Kind: "PersistentVolume", + APIVersion: "v1", + }, + ObjectMeta: objectMeta("", name), + } +} + +func NewSecret(ns, name string) *corev1.Secret { + return &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: objectMeta(ns, name), + } +} + +func NewDeployment(ns, name string) *appsv1.Deployment { + return &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + APIVersion: "apps/v1", + }, + ObjectMeta: objectMeta(ns, name), + } +} + +func objectMeta(ns, name string) metav1.ObjectMeta { + return metav1.ObjectMeta{ + Namespace: ns, + Name: name, + } +}