From 48711f8f69fa1e02018624adef47dda0465cd7c0 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Wed, 5 Jul 2023 20:44:09 +0800 Subject: [PATCH] Remove dependency of the legacy client code. Signed-off-by: Xun Jiang --- pkg/apis/velero/v1/constants.go | 8 ++ pkg/cmd/cli/backup/create.go | 27 +++-- pkg/cmd/cli/backup/create_test.go | 163 +++++++++++++++--------------- 3 files changed, 108 insertions(+), 90 deletions(-) diff --git a/pkg/apis/velero/v1/constants.go b/pkg/apis/velero/v1/constants.go index a7292d5684..5dbff321da 100644 --- a/pkg/apis/velero/v1/constants.go +++ b/pkg/apis/velero/v1/constants.go @@ -16,6 +16,8 @@ limitations under the License. package v1 +import "k8s.io/apimachinery/pkg/runtime/schema" + const ( // DefaultNamespace is the Kubernetes namespace that is used by default for // the Velero server and API objects. @@ -47,3 +49,9 @@ const ( // APIGroupVersionsFeatureFlag is the feature flag string that defines whether or not to handle multiple API Group Versions APIGroupVersionsFeatureFlag = "EnableAPIGroupVersions" ) + +var ( + BackupGVR = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "backups"} + ScheduleGVR = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "schedules"} + VolumeSnapshotLocationGVR = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "volumesnapshotlocations"} +) diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index 2b1a1c766b..65d8e025d6 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -27,7 +27,11 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" kubeerrs "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/dynamic/dynamicinformer" "k8s.io/client-go/tools/cache" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" @@ -36,8 +40,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/cmd" "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" "github.com/vmware-tanzu/velero/pkg/cmd/util/output" - veleroclient "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - v1 "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" "github.com/vmware-tanzu/velero/pkg/util/collections" ) @@ -107,7 +109,7 @@ type CreateOptions struct { CSISnapshotTimeout time.Duration ItemOperationTimeout time.Duration ResPoliciesConfigmap string - client veleroclient.Interface + client dynamic.Interface } func NewCreateOptions() *CreateOptions { @@ -203,7 +205,7 @@ func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Facto } for _, loc := range o.SnapshotLocations { - if _, err := o.client.VeleroV1().VolumeSnapshotLocations(f.Namespace()).Get(context.TODO(), loc, metav1.GetOptions{}); err != nil { + if _, err := o.client.Resource(velerov1api.VolumeSnapshotLocationGVR).Namespace(f.Namespace()).Get(context.TODO(), loc, metav1.GetOptions{}); err != nil { return err } } @@ -216,7 +218,7 @@ func (o *CreateOptions) Complete(args []string, f client.Factory) error { if len(args) > 0 { o.Name = args[0] } - client, err := f.Client() + client, err := f.DynamicClient() if err != nil { return err } @@ -246,7 +248,7 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { updates = make(chan *velerov1api.Backup) - backupInformer = v1.NewBackupInformer(o.client, f.Namespace(), 0, nil) + backupInformer = dynamicinformer.NewFilteredDynamicSharedInformerFactory(o.client, 0, f.Namespace(), nil).ForResource(velerov1api.BackupGVR).Informer() backupInformer.AddEventHandler( cache.FilteringResourceEventHandler{ @@ -278,7 +280,11 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { go backupInformer.Run(stop) } - _, err = o.client.VeleroV1().Backups(backup.Namespace).Create(context.TODO(), backup, metav1.CreateOptions{}) + backupObjMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(backup) + if err != nil { + return err + } + _, err = o.client.Resource(velerov1api.BackupGVR).Namespace(backup.Namespace).Create(context.TODO(), &unstructured.Unstructured{Object: backupObjMap}, metav1.CreateOptions{}) if err != nil { return err } @@ -341,7 +347,12 @@ func (o *CreateOptions) BuildBackup(namespace string) (*velerov1api.Backup, erro var backupBuilder *builder.BackupBuilder if o.FromSchedule != "" { - schedule, err := o.client.VeleroV1().Schedules(namespace).Get(context.TODO(), o.FromSchedule, metav1.GetOptions{}) + obj, err := o.client.Resource(velerov1api.ScheduleGVR).Namespace(namespace).Get(context.TODO(), o.FromSchedule, metav1.GetOptions{}) + if err != nil { + return nil, err + } + schedule := new(velerov1api.Schedule) + err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), schedule) if err != nil { return nil, err } diff --git a/pkg/cmd/cli/backup/create_test.go b/pkg/cmd/cli/backup/create_test.go index bcff3a98c4..76f824a9a7 100644 --- a/pkg/cmd/cli/backup/create_test.go +++ b/pkg/cmd/cli/backup/create_test.go @@ -23,23 +23,21 @@ import ( "testing" "time" + flag "github.com/spf13/pflag" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - flag "github.com/spf13/pflag" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + dynamicfake "k8s.io/client-go/dynamic/fake" + clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" - - clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake" - factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks" cmdtest "github.com/vmware-tanzu/velero/pkg/cmd/test" - "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake" - versionedmocks "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/mocks" "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" - velerov1mocks "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1/mocks" "github.com/vmware-tanzu/velero/pkg/test" ) @@ -77,27 +75,32 @@ func TestCreateOptions_BuildBackup(t *testing.T) { func TestCreateOptions_BuildBackupFromSchedule(t *testing.T) { o := NewCreateOptions() o.FromSchedule = "test" - o.client = fake.NewSimpleClientset() + + scheme := runtime.NewScheme() + err := velerov1api.AddToScheme(scheme) + require.NoError(t, err) + o.client = dynamicfake.NewSimpleDynamicClient(scheme) t.Run("inexistent schedule", func(t *testing.T) { _, err := o.BuildBackup(cmdtest.VeleroNameSpace) - assert.Error(t, err) + require.Error(t, err) }) expectedBackupSpec := builder.ForBackup("test", cmdtest.VeleroNameSpace).IncludedNamespaces("test").Result().Spec schedule := builder.ForSchedule(cmdtest.VeleroNameSpace, "test").Template(expectedBackupSpec).ObjectMeta(builder.WithLabels("velero.io/test", "true"), builder.WithAnnotations("velero.io/test", "true")).Result() - o.client.VeleroV1().Schedules(cmdtest.VeleroNameSpace).Create(context.TODO(), schedule, metav1.CreateOptions{}) + scheduleObjMap, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(schedule) + o.client.Resource(velerov1api.ScheduleGVR).Namespace(cmdtest.VeleroNameSpace).Create(context.TODO(), &unstructured.Unstructured{Object: scheduleObjMap}, metav1.CreateOptions{}) t.Run("existing schedule", func(t *testing.T) { backup, err := o.BuildBackup(cmdtest.VeleroNameSpace) - assert.NoError(t, err) + require.NoError(t, err) - assert.Equal(t, expectedBackupSpec, backup.Spec) - assert.Equal(t, map[string]string{ + require.Equal(t, expectedBackupSpec, backup.Spec) + require.Equal(t, map[string]string{ "velero.io/test": "true", velerov1api.ScheduleNameLabel: "test", }, backup.GetLabels()) - assert.Equal(t, map[string]string{ + require.Equal(t, map[string]string{ "velero.io/test": "true", }, backup.GetAnnotations()) }) @@ -145,6 +148,7 @@ func TestCreateCommand(t *testing.T) { args := []string{name} t.Run("create a backup create command with full options except fromSchedule and wait, then run by create option", func(t *testing.T) { + // create a factory f := &factorymocks.Factory{} @@ -203,79 +207,76 @@ func TestCreateCommand(t *testing.T) { flags.Parse([]string{"--data-mover", dataMover}) //flags.Parse([]string{"--wait"}) - backups := &velerov1mocks.BackupInterface{} - veleroV1 := &velerov1mocks.VeleroV1Interface{} - client := &versionedmocks.Interface{} - bk := &velerov1api.Backup{} - backups.On("Create", mock.Anything, mock.Anything, mock.Anything).Return(bk, nil) - veleroV1.On("Backups", mock.Anything).Return(backups, nil) - client.On("VeleroV1").Return(veleroV1, nil) - f.On("Client").Return(client, nil) + scheme := runtime.NewScheme() + err := velerov1api.AddToScheme(scheme) + require.NoError(t, err) + client := dynamicfake.NewSimpleDynamicClient(scheme) + + f.On("DynamicClient").Return(client, nil) f.On("Namespace").Return(mock.Anything) f.On("KubebuilderClient").Return(nil, nil) //Complete e := o.Complete(args, f) - assert.NoError(t, e) + require.NoError(t, e) //Validate e = o.Validate(cmd, args, f) - assert.Contains(t, e.Error(), "include-resources, exclude-resources and include-cluster-resources are old filter parameters") - assert.Contains(t, e.Error(), "include-cluster-scoped-resources, exclude-cluster-scoped-resources, include-namespace-scoped-resources and exclude-namespace-scoped-resources are new filter parameters.\nThey cannot be used together") + require.Contains(t, e.Error(), "include-resources, exclude-resources and include-cluster-resources are old filter parameters") + require.Contains(t, e.Error(), "include-cluster-scoped-resources, exclude-cluster-scoped-resources, include-namespace-scoped-resources and exclude-namespace-scoped-resources are new filter parameters.\nThey cannot be used together") //cmd e = o.Run(cmd, f) - assert.NoError(t, e) + require.NoError(t, e) //Execute cmd.SetArgs([]string{"bk-name-exe"}) e = cmd.Execute() - assert.NoError(t, e) + require.NoError(t, e) // verify all options are set as expected - assert.Equal(t, name, o.Name) - assert.Equal(t, includeNamespaces, o.IncludeNamespaces.String()) - assert.Equal(t, excludeNamespaces, o.ExcludeNamespaces.String()) - assert.Equal(t, includeResources, o.IncludeResources.String()) - assert.Equal(t, excludeResources, o.ExcludeResources.String()) - assert.Equal(t, includeClusterScopedResources, o.IncludeClusterScopedResources.String()) - assert.Equal(t, excludeClusterScopedResources, o.ExcludeClusterScopedResources.String()) - assert.Equal(t, includeNamespaceScopedResources, o.IncludeNamespaceScopedResources.String()) - assert.Equal(t, excludeNamespaceScopedResources, o.ExcludeNamespaceScopedResources.String()) - assert.Equal(t, true, test.CompareSlice(strings.Split(labels, ","), strings.Split(o.Labels.String(), ","))) - assert.Equal(t, storageLocation, o.StorageLocation) - assert.Equal(t, snapshotLocations, strings.Split(o.SnapshotLocations[0], ",")[0]) - assert.Equal(t, selector, o.Selector.String()) - assert.Equal(t, orderedResources, o.OrderedResources) - assert.Equal(t, csiSnapshotTimeout, o.CSISnapshotTimeout.String()) - assert.Equal(t, itemOperationTimeout, o.ItemOperationTimeout.String()) - assert.Equal(t, snapshotVolumes, o.SnapshotVolumes.String()) - assert.Equal(t, snapshotMoveData, o.SnapshotMoveData.String()) - assert.Equal(t, includeClusterResources, o.IncludeClusterResources.String()) - assert.Equal(t, defaultVolumesToFsBackup, o.DefaultVolumesToFsBackup.String()) - assert.Equal(t, resPoliciesConfigmap, o.ResPoliciesConfigmap) - assert.Equal(t, dataMover, o.DataMover) + require.Equal(t, name, o.Name) + require.Equal(t, includeNamespaces, o.IncludeNamespaces.String()) + require.Equal(t, excludeNamespaces, o.ExcludeNamespaces.String()) + require.Equal(t, includeResources, o.IncludeResources.String()) + require.Equal(t, excludeResources, o.ExcludeResources.String()) + require.Equal(t, includeClusterScopedResources, o.IncludeClusterScopedResources.String()) + require.Equal(t, excludeClusterScopedResources, o.ExcludeClusterScopedResources.String()) + require.Equal(t, includeNamespaceScopedResources, o.IncludeNamespaceScopedResources.String()) + require.Equal(t, excludeNamespaceScopedResources, o.ExcludeNamespaceScopedResources.String()) + require.Equal(t, true, test.CompareSlice(strings.Split(labels, ","), strings.Split(o.Labels.String(), ","))) + require.Equal(t, storageLocation, o.StorageLocation) + require.Equal(t, snapshotLocations, strings.Split(o.SnapshotLocations[0], ",")[0]) + require.Equal(t, selector, o.Selector.String()) + require.Equal(t, orderedResources, o.OrderedResources) + require.Equal(t, csiSnapshotTimeout, o.CSISnapshotTimeout.String()) + require.Equal(t, itemOperationTimeout, o.ItemOperationTimeout.String()) + require.Equal(t, snapshotVolumes, o.SnapshotVolumes.String()) + require.Equal(t, snapshotMoveData, o.SnapshotMoveData.String()) + require.Equal(t, includeClusterResources, o.IncludeClusterResources.String()) + require.Equal(t, defaultVolumesToFsBackup, o.DefaultVolumesToFsBackup.String()) + require.Equal(t, resPoliciesConfigmap, o.ResPoliciesConfigmap) + require.Equal(t, dataMover, o.DataMover) //assert.Equal(t, true, o.Wait) // verify oldAndNewFilterParametersUsedTogether mix := o.oldAndNewFilterParametersUsedTogether() - assert.Equal(t, true, mix) + require.Equal(t, true, mix) }) + t.Run("create a backup create command with specific storage-location setting", func(t *testing.T) { bsl := "bsl-1" // create a factory f := &factorymocks.Factory{} cmd := NewCreateCommand(f, "") - backups := &velerov1mocks.BackupInterface{} - veleroV1 := &velerov1mocks.VeleroV1Interface{} - client := &versionedmocks.Interface{} kbclient := clientfake.NewClientBuilder().WithScheme(scheme.Scheme).Build() - bk := &velerov1api.Backup{} - backups.On("Create", mock.Anything, mock.Anything, mock.Anything).Return(bk, nil) - veleroV1.On("Backups", mock.Anything).Return(backups, nil) - client.On("VeleroV1").Return(veleroV1, nil) - f.On("Client").Return(client, nil) + scheme := runtime.NewScheme() + err := velerov1api.AddToScheme(scheme) + require.NoError(t, err) + client := dynamicfake.NewSimpleDynamicClient(scheme) + + f.On("DynamicClient").Return(client, nil) f.On("Namespace").Return(mock.Anything) f.On("KubebuilderClient").Return(kbclient, nil) @@ -301,17 +302,18 @@ func TestCreateCommand(t *testing.T) { // create a factory f := &factorymocks.Factory{} cmd := NewCreateCommand(f, "") - vsls := &velerov1mocks.VolumeSnapshotLocationInterface{} - veleroV1 := &velerov1mocks.VeleroV1Interface{} - client := &versionedmocks.Interface{} kbclient := clientfake.NewClientBuilder().WithScheme(scheme.Scheme).Build() - vsl := &velerov1api.VolumeSnapshotLocation{} - vsls.On("Get", mock.Anything, mock.Anything, mock.Anything).Return(vsl, nil) - veleroV1.On("VolumeSnapshotLocations", mock.Anything).Return(vsls, nil) - client.On("VeleroV1").Return(veleroV1, nil) - f.On("Client").Return(client, nil) - f.On("Namespace").Return(mock.Anything) + scheme := runtime.NewScheme() + err := velerov1api.AddToScheme(scheme) + require.NoError(t, err) + client := dynamicfake.NewSimpleDynamicClient(scheme) + vsl := builder.ForVolumeSnapshotLocation(cmdtest.VeleroNameSpace, vslName).Result() + vslObjMap, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(vsl) + client.Resource(velerov1api.VolumeSnapshotLocationGVR).Namespace(cmdtest.VeleroNameSpace).Create(context.Background(), &unstructured.Unstructured{Object: vslObjMap}, metav1.CreateOptions{}) + + f.On("DynamicClient").Return(client, nil) + f.On("Namespace").Return(cmdtest.VeleroNameSpace) f.On("KubebuilderClient").Return(kbclient, nil) flags := new(flag.FlagSet) @@ -343,21 +345,18 @@ func TestCreateCommand(t *testing.T) { fromSchedule := "schedule-name-1" flags.Parse([]string{"--from-schedule", fromSchedule}) - backups := &velerov1mocks.BackupInterface{} - bk := &velerov1api.Backup{} - schedules := &velerov1mocks.ScheduleInterface{} - veleroV1 := &velerov1mocks.VeleroV1Interface{} - client := &versionedmocks.Interface{} kbclient := clientfake.NewClientBuilder().WithScheme(scheme.Scheme).Build() - sd := &velerov1api.Schedule{} - - backups.On("Create", mock.Anything, mock.Anything, mock.Anything).Return(bk, nil) - veleroV1.On("Backups", mock.Anything).Return(backups, nil) - schedules.On("Get", mock.Anything, mock.Anything, mock.Anything).Return(sd, nil) - veleroV1.On("Schedules", mock.Anything).Return(schedules, nil) - client.On("VeleroV1").Return(veleroV1, nil) - f.On("Client").Return(client, nil) - f.On("Namespace").Return(mock.Anything) + + scheme := runtime.NewScheme() + err := velerov1api.AddToScheme(scheme) + require.NoError(t, err) + client := dynamicfake.NewSimpleDynamicClient(scheme) + schedule := builder.ForSchedule(cmdtest.VeleroNameSpace, fromSchedule).Result() + scheduleObjMap, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(schedule) + client.Resource(velerov1api.ScheduleGVR).Namespace(cmdtest.VeleroNameSpace).Create(context.Background(), &unstructured.Unstructured{Object: scheduleObjMap}, metav1.CreateOptions{}) + + f.On("DynamicClient").Return(client, nil) + f.On("Namespace").Return(cmdtest.VeleroNameSpace) f.On("KubebuilderClient").Return(kbclient, nil) e := o.Complete(args, f)