diff --git a/changelogs/unreleased/6469-blackpiglet b/changelogs/unreleased/6469-blackpiglet new file mode 100644 index 0000000000..b5349c5179 --- /dev/null +++ b/changelogs/unreleased/6469-blackpiglet @@ -0,0 +1 @@ +Remove dependency of the legacy client code from pkg/cmd directory \ No newline at end of file diff --git a/pkg/client/factory.go b/pkg/client/factory.go index e57ddc4fe5..5c1ffc545c 100644 --- a/pkg/client/factory.go +++ b/pkg/client/factory.go @@ -55,6 +55,10 @@ type Factory interface { // types to its scheme. It uses the following priority to specify the cluster // configuration: --kubeconfig flag, KUBECONFIG environment variable, in-cluster configuration. KubebuilderClient() (kbclient.Client, error) + // KubebuilderWatchClient returns a client with watcher for the controller runtime framework. + // It adds Kubernetes and Velero types to its scheme. It uses the following priority to specify the cluster + // configuration: --kubeconfig flag, KUBECONFIG environment variable, in-cluster configuration. + KubebuilderWatchClient() (kbclient.WithWatch, error) // SetBasename changes the basename for an already-constructed client. // This is useful for generating clients that require a different user-agent string below the root `velero` // command, such as the server subcommand. @@ -182,6 +186,39 @@ func (f *factory) KubebuilderClient() (kbclient.Client, error) { return kubebuilderClient, nil } +func (f *factory) KubebuilderWatchClient() (kbclient.WithWatch, error) { + clientConfig, err := f.ClientConfig() + if err != nil { + return nil, err + } + + scheme := runtime.NewScheme() + if err := velerov1api.AddToScheme(scheme); err != nil { + return nil, err + } + if err := velerov2alpha1api.AddToScheme(scheme); err != nil { + return nil, err + } + if err := k8scheme.AddToScheme(scheme); err != nil { + return nil, err + } + if err := apiextv1beta1.AddToScheme(scheme); err != nil { + return nil, err + } + if err := apiextv1.AddToScheme(scheme); err != nil { + return nil, err + } + kubebuilderWatchClient, err := kbclient.NewWithWatch(clientConfig, kbclient.Options{ + Scheme: scheme, + }) + + if err != nil { + return nil, err + } + + return kubebuilderWatchClient, nil +} + func (f *factory) SetBasename(name string) { f.baseName = name } diff --git a/pkg/client/mocks/Factory.go b/pkg/client/mocks/Factory.go index 27c588cf44..3bd0160476 100644 --- a/pkg/client/mocks/Factory.go +++ b/pkg/client/mocks/Factory.go @@ -157,6 +157,32 @@ func (_m *Factory) KubebuilderClient() (pkgclient.Client, error) { return r0, r1 } +// KubebuilderWatchClient provides a mock function with given fields: +func (_m *Factory) KubebuilderWatchClient() (pkgclient.WithWatch, error) { + ret := _m.Called() + + var r0 pkgclient.WithWatch + var r1 error + if rf, ok := ret.Get(0).(func() (pkgclient.WithWatch, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() pkgclient.WithWatch); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(pkgclient.WithWatch) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Namespace provides a mock function with given fields: func (_m *Factory) Namespace() string { ret := _m.Called() diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index 2b1a1c766b..7a5de6984d 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -22,13 +22,11 @@ import ( "strings" "time" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/spf13/cobra" "github.com/spf13/pflag" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kubeerrs "k8s.io/apimachinery/pkg/util/errors" "k8s.io/client-go/tools/cache" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" @@ -36,9 +34,8 @@ 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" + "github.com/vmware-tanzu/velero/pkg/util/kube" ) func NewCreateCommand(f client.Factory, use string) *cobra.Command { @@ -107,7 +104,7 @@ type CreateOptions struct { CSISnapshotTimeout time.Duration ItemOperationTimeout time.Duration ResPoliciesConfigmap string - client veleroclient.Interface + client kbclient.WithWatch } func NewCreateOptions() *CreateOptions { @@ -171,7 +168,7 @@ func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Facto return err } - client, err := f.KubebuilderClient() + client, err := f.KubebuilderWatchClient() if err != nil { return err } @@ -203,7 +200,8 @@ 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 { + snapshotLocation := new(velerov1api.VolumeSnapshotLocation) + if err := o.client.Get(context.TODO(), kbclient.ObjectKey{Namespace: f.Namespace(), Name: loc}, snapshotLocation); err != nil { return err } } @@ -216,7 +214,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.KubebuilderWatchClient() if err != nil { return err } @@ -238,7 +236,6 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { fmt.Println("Creating backup from schedule, all other filters are ignored.") } - var backupInformer cache.SharedIndexInformer var updates chan *velerov1api.Backup if o.Wait { stop := make(chan struct{}) @@ -246,12 +243,16 @@ 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) - + lw := kube.InternalLW{ + Client: o.client, + Namespace: f.Namespace(), + } + backupInformer := cache.NewSharedInformer(&lw, &velerov1api.Backup{}, time.Second) backupInformer.AddEventHandler( cache.FilteringResourceEventHandler{ FilterFunc: func(obj interface{}) bool { backup, ok := obj.(*velerov1api.Backup) + if !ok { return false } @@ -275,10 +276,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{}) + err = o.client.Create(context.TODO(), backup, &kbclient.CreateOptions{}) if err != nil { return err } @@ -341,7 +343,8 @@ 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{}) + schedule := new(velerov1api.Schedule) + err := o.client.Get(context.TODO(), kbclient.ObjectKey{Namespace: namespace, Name: o.FromSchedule}, 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..8d20cda18d 100644 --- a/pkg/cmd/cli/backup/create_test.go +++ b/pkg/cmd/cli/backup/create_test.go @@ -23,24 +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/runtime" + controllerclient "sigs.k8s.io/controller-runtime/pkg/client" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" 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" + velerotest "github.com/vmware-tanzu/velero/pkg/test" ) func TestCreateOptions_BuildBackup(t *testing.T) { @@ -77,27 +74,31 @@ 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 = velerotest.NewFakeControllerRuntimeClient(t).(controllerclient.WithWatch) 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{}) + o.client.Create(context.TODO(), schedule, &kbclient.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 +146,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,81 +205,68 @@ 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) + client := velerotest.NewFakeControllerRuntimeClient(t).(kbclient.WithWatch) + f.On("Namespace").Return(mock.Anything) - f.On("KubebuilderClient").Return(nil, nil) + f.On("KubebuilderWatchClient").Return(client, 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) + kbclient := velerotest.NewFakeControllerRuntimeClient(t).(kbclient.WithWatch) + f.On("Namespace").Return(mock.Anything) - f.On("KubebuilderClient").Return(kbclient, nil) + f.On("KubebuilderWatchClient").Return(kbclient, nil) flags := new(flag.FlagSet) o := NewCreateOptions() @@ -301,18 +290,14 @@ 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) - f.On("KubebuilderClient").Return(kbclient, nil) + kbclient := velerotest.NewFakeControllerRuntimeClient(t).(kbclient.WithWatch) + + vsl := builder.ForVolumeSnapshotLocation(cmdtest.VeleroNameSpace, vslName).Result() + + kbclient.Create(cmd.Context(), vsl, &controllerclient.CreateOptions{}) + + f.On("Namespace").Return(cmdtest.VeleroNameSpace) + f.On("KubebuilderWatchClient").Return(kbclient, nil) flags := new(flag.FlagSet) o := NewCreateOptions() @@ -343,22 +328,13 @@ 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) - f.On("KubebuilderClient").Return(kbclient, nil) + kbclient := velerotest.NewFakeControllerRuntimeClient(t).(kbclient.WithWatch) + + schedule := builder.ForSchedule(cmdtest.VeleroNameSpace, fromSchedule).Result() + kbclient.Create(context.Background(), schedule, &controllerclient.CreateOptions{}) + + f.On("Namespace").Return(cmdtest.VeleroNameSpace) + f.On("KubebuilderWatchClient").Return(kbclient, nil) e := o.Complete(args, f) assert.NoError(t, e) diff --git a/pkg/cmd/cli/backup/delete.go b/pkg/cmd/cli/backup/delete.go index a3e816bbcc..a79950f5b1 100644 --- a/pkg/cmd/cli/backup/delete.go +++ b/pkg/cmd/cli/backup/delete.go @@ -25,12 +25,14 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" kubeerrs "k8s.io/apimachinery/pkg/util/errors" + controllerclient "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/backup" + "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/cmd" "github.com/vmware-tanzu/velero/pkg/cmd/cli" + "github.com/vmware-tanzu/velero/pkg/label" ) // NewDeleteCommand creates a new command that deletes a backup. @@ -82,7 +84,8 @@ func Run(o *cli.DeleteOptions) error { switch { case len(o.Names) > 0: for _, name := range o.Names { - backup, err := o.Client.VeleroV1().Backups(o.Namespace).Get(context.TODO(), name, metav1.GetOptions{}) + backup := new(velerov1api.Backup) + err := o.Client.Get(context.TODO(), controllerclient.ObjectKey{Namespace: o.Namespace, Name: name}, backup) if err != nil { errs = append(errs, errors.WithStack(err)) continue @@ -91,17 +94,22 @@ func Run(o *cli.DeleteOptions) error { backups = append(backups, backup) } default: - selector := labels.Everything().String() + selector := labels.Everything() if o.Selector.LabelSelector != nil { - selector = o.Selector.String() + convertedSelector, err := metav1.LabelSelectorAsSelector(o.Selector.LabelSelector) + if err != nil { + return errors.WithStack(err) + } + selector = convertedSelector } - res, err := o.Client.VeleroV1().Backups(o.Namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: selector}) + backupList := new(velerov1api.BackupList) + err := o.Client.List(context.TODO(), backupList, &controllerclient.ListOptions{LabelSelector: selector}) if err != nil { return errors.WithStack(err) } - for i := range res.Items { - backups = append(backups, &res.Items[i]) + for i := range backupList.Items { + backups = append(backups, &backupList.Items[i]) } } @@ -112,9 +120,11 @@ func Run(o *cli.DeleteOptions) error { // create a backup deletion request for each for _, b := range backups { - deleteRequest := backup.NewDeleteBackupRequest(b.Name, string(b.UID)) + deleteRequest := builder.ForDeleteBackupRequest(o.Namespace, "").BackupName(b.Name). + ObjectMeta(builder.WithLabels(velerov1api.BackupNameLabel, label.GetValidName(b.Name), + velerov1api.BackupUIDLabel, string(b.UID)), builder.WithGenerateName(b.Name+"-")).Result() - if _, err := o.Client.VeleroV1().DeleteBackupRequests(o.Namespace).Create(context.TODO(), deleteRequest, metav1.CreateOptions{}); err != nil { + if err := o.Client.Create(context.TODO(), deleteRequest, &controllerclient.CreateOptions{}); err != nil { errs = append(errs, err) continue } diff --git a/pkg/cmd/cli/backup/delete_test.go b/pkg/cmd/cli/backup/delete_test.go index 7d479aaaca..f8aae01659 100644 --- a/pkg/cmd/cli/backup/delete_test.go +++ b/pkg/cmd/cli/backup/delete_test.go @@ -17,21 +17,21 @@ limitations under the License. package backup import ( + "context" "fmt" "os" "os/exec" "testing" flag "github.com/spf13/pflag" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + controllerclient "sigs.k8s.io/controller-runtime/pkg/client" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/builder" factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks" "github.com/vmware-tanzu/velero/pkg/cmd/cli" cmdtest "github.com/vmware-tanzu/velero/pkg/cmd/test" - versionedmocks "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/mocks" - velerov1mocks "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1/mocks" + velerotest "github.com/vmware-tanzu/velero/pkg/test" veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" ) @@ -41,44 +41,35 @@ func TestDeleteCommand(t *testing.T) { // create a factory f := &factorymocks.Factory{} - deleteBackupRequest := &velerov1mocks.DeleteBackupRequestInterface{} - backups := &velerov1mocks.BackupInterface{} - veleroV1 := &velerov1mocks.VeleroV1Interface{} - client := &versionedmocks.Interface{} - bk := &velerov1api.Backup{} - dbr := &velerov1api.DeleteBackupRequest{} - backups.On("Get", mock.Anything, mock.Anything, mock.Anything).Return(bk, nil) - deleteBackupRequest.On("Create", mock.Anything, mock.Anything, mock.Anything).Return(dbr, nil) - veleroV1.On("DeleteBackupRequests", mock.Anything).Return(deleteBackupRequest, nil) - veleroV1.On("Backups", mock.Anything).Return(backups, nil) - client.On("VeleroV1").Return(veleroV1, nil) - f.On("Client").Return(client, nil) - f.On("Namespace").Return(mock.Anything) + client := velerotest.NewFakeControllerRuntimeClient(t) + client.Create(context.Background(), builder.ForBackup(cmdtest.VeleroNameSpace, backupName).Result(), &controllerclient.CreateOptions{}) + + f.On("KubebuilderClient").Return(client, nil) + f.On("Namespace").Return(cmdtest.VeleroNameSpace) // create command c := NewDeleteCommand(f, "velero backup delete") c.SetArgs([]string{backupName}) - assert.Equal(t, "Delete backups", c.Short) + require.Equal(t, "Delete backups", c.Short) o := cli.NewDeleteOptions("backup") flags := new(flag.FlagSet) o.BindFlags(flags) flags.Parse([]string{"--confirm"}) - args := []string{"bk1", "bk2"} + args := []string{backupName} - bk.Name = backupName e := o.Complete(f, args) - assert.Equal(t, e, nil) + require.Equal(t, nil, e) e = o.Validate(c, f, args) - assert.Equal(t, e, nil) + require.Equal(t, nil, e) e = Run(o) - assert.Equal(t, e, nil) + require.Equal(t, nil, e) e = c.Execute() - assert.Equal(t, e, nil) + require.Equal(t, nil, e) if os.Getenv(cmdtest.CaptureFlag) == "1" { return @@ -89,7 +80,7 @@ func TestDeleteCommand(t *testing.T) { stdout, _, err := veleroexec.RunCommand(cmd) if err == nil { - assert.Contains(t, stdout, fmt.Sprintf("Request to delete backup \"%s\" submitted successfully.", backupName)) + require.Contains(t, stdout, fmt.Sprintf("Request to delete backup \"%s\" submitted successfully.", backupName)) return } t.Fatalf("process ran with err %v, want backups by get()", err) diff --git a/pkg/cmd/cli/backup/describe.go b/pkg/cmd/cli/backup/describe.go index e6af647dfc..ec84098b28 100644 --- a/pkg/cmd/cli/backup/describe.go +++ b/pkg/cmd/cli/backup/describe.go @@ -21,14 +21,14 @@ import ( "fmt" "os" - "github.com/spf13/cobra" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" snapshotv1client "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned" + "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + controllerclient "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/cmd" "github.com/vmware-tanzu/velero/pkg/cmd/util/output" @@ -54,9 +54,6 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { Use: use + " [NAME1] [NAME2] [NAME...]", Short: "Describe backups", Run: func(c *cobra.Command, args []string) { - veleroClient, err := f.Client() - cmd.CheckError(err) - kbClient, err := f.KubebuilderClient() cmd.CheckError(err) @@ -64,29 +61,36 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { cmd.CheckError(fmt.Errorf("invalid output format '%s'. valid value are 'plaintext, json'", outputFormat)) } - var backups *velerov1api.BackupList + backups := new(velerov1api.BackupList) if len(args) > 0 { - backups = new(velerov1api.BackupList) for _, name := range args { - backup, err := veleroClient.VeleroV1().Backups(f.Namespace()).Get(context.TODO(), name, metav1.GetOptions{}) + backup := new(velerov1api.Backup) + err := kbClient.Get(context.TODO(), controllerclient.ObjectKey{Namespace: f.Namespace(), Name: name}, backup) cmd.CheckError(err) backups.Items = append(backups.Items, *backup) } } else { - backups, err = veleroClient.VeleroV1().Backups(f.Namespace()).List(context.TODO(), listOptions) + parsedSelector, err := labels.Parse(listOptions.LabelSelector) + cmd.CheckError(err) + err = kbClient.List(context.TODO(), backups, &controllerclient.ListOptions{LabelSelector: parsedSelector}) cmd.CheckError(err) } first := true for i, backup := range backups.Items { - deleteRequestListOptions := pkgbackup.NewDeleteBackupRequestListOptions(backup.Name, string(backup.UID)) - deleteRequestList, err := veleroClient.VeleroV1().DeleteBackupRequests(f.Namespace()).List(context.TODO(), deleteRequestListOptions) + deleteRequestList := new(velerov1api.DeleteBackupRequestList) + err := kbClient.List(context.TODO(), deleteRequestList, &controllerclient.ListOptions{ + Namespace: f.Namespace(), + LabelSelector: labels.SelectorFromSet(map[string]string{velerov1api.BackupNameLabel: label.GetValidName(backup.Name), velerov1api.BackupUIDLabel: string(backup.UID)}), + }) if err != nil { fmt.Fprintf(os.Stderr, "error getting DeleteBackupRequests for backup %s: %v\n", backup.Name, err) } - opts := label.NewListOptionsForBackup(backup.Name) - podVolumeBackupList, err := veleroClient.VeleroV1().PodVolumeBackups(f.Namespace()).List(context.TODO(), opts) + podVolumeBackupList := new(velerov1api.PodVolumeBackupList) + err = kbClient.List(context.TODO(), podVolumeBackupList, &controllerclient.ListOptions{ + LabelSelector: labels.SelectorFromSet(map[string]string{velerov1api.BackupNameLabel: label.GetValidName(backup.Name)}), + }) if err != nil { fmt.Fprintf(os.Stderr, "error getting PodVolumeBackups for backup %s: %v\n", backup.Name, err) } @@ -101,6 +105,7 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { csiClient, err = snapshotv1client.NewForConfig(clientConfig) cmd.CheckError(err) + opts := label.NewListOptionsForBackup(backup.Name) vscList, err = csiClient.SnapshotV1().VolumeSnapshotContents().List(context.TODO(), opts) if err != nil { fmt.Fprintf(os.Stderr, "error getting VolumeSnapshotContent objects for backup %s: %v\n", backup.Name, err) @@ -110,10 +115,10 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { // structured output only applies to a single backup in case of OOM // To describe the list of backups in structured format, users could iterate over the list and describe backup one after another. if len(backups.Items) == 1 && outputFormat != "plaintext" { - s := output.DescribeBackupInSF(context.Background(), kbClient, &backups.Items[i], deleteRequestList.Items, podVolumeBackupList.Items, vscList.Items, details, veleroClient, insecureSkipTLSVerify, caCertFile, outputFormat) + s := output.DescribeBackupInSF(context.Background(), kbClient, &backups.Items[i], deleteRequestList.Items, podVolumeBackupList.Items, vscList.Items, details, insecureSkipTLSVerify, caCertFile, outputFormat) fmt.Print(s) } else { - s := output.DescribeBackup(context.Background(), kbClient, &backups.Items[i], deleteRequestList.Items, podVolumeBackupList.Items, vscList.Items, details, veleroClient, insecureSkipTLSVerify, caCertFile) + s := output.DescribeBackup(context.Background(), kbClient, &backups.Items[i], deleteRequestList.Items, podVolumeBackupList.Items, vscList.Items, details, insecureSkipTLSVerify, caCertFile) if first { first = false fmt.Print(s) diff --git a/pkg/cmd/cli/backup/describe_test.go b/pkg/cmd/cli/backup/describe_test.go index d90285dc03..3272c68009 100644 --- a/pkg/cmd/cli/backup/describe_test.go +++ b/pkg/cmd/cli/backup/describe_test.go @@ -17,55 +17,37 @@ limitations under the License. package backup import ( + "context" "fmt" "os" "os/exec" "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "k8s.io/client-go/rest" + controllerclient "sigs.k8s.io/controller-runtime/pkg/client" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/builder" factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks" cmdtest "github.com/vmware-tanzu/velero/pkg/cmd/test" "github.com/vmware-tanzu/velero/pkg/features" - versionedmocks "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/mocks" - velerov1mocks "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1/mocks" + "github.com/vmware-tanzu/velero/pkg/test" veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" ) func TestNewDescribeCommand(t *testing.T) { // create a factory f := &factorymocks.Factory{} + backupName := "bk-describe-1" + testBackup := builder.ForBackup(cmdtest.VeleroNameSpace, backupName).Result() - backups := &velerov1mocks.BackupInterface{} - veleroV1 := &velerov1mocks.VeleroV1Interface{} - client := &versionedmocks.Interface{} clientConfig := rest.Config{} + kbClient := test.NewFakeControllerRuntimeClient(t) + kbClient.Create(context.Background(), testBackup, &controllerclient.CreateOptions{}) - deleteBackupRequest := &velerov1mocks.DeleteBackupRequestInterface{} - bk := &velerov1api.Backup{} - bkList := &velerov1api.BackupList{} - deleteBackupRequestList := &velerov1api.DeleteBackupRequestList{} - podVolumeBackups := &velerov1mocks.PodVolumeBackupInterface{} - podVolumeBackupList := &velerov1api.PodVolumeBackupList{} - - backupName := "bk-describe-1" - bk.Name = backupName - - backups.On("List", mock.Anything, mock.Anything).Return(bkList, nil) - backups.On("Get", mock.Anything, mock.Anything, mock.Anything).Return(bk, nil) - veleroV1.On("Backups", mock.Anything).Return(backups, nil) - deleteBackupRequest.On("List", mock.Anything, mock.Anything).Return(deleteBackupRequestList, nil) - veleroV1.On("DeleteBackupRequests", mock.Anything).Return(deleteBackupRequest, nil) - podVolumeBackups.On("List", mock.Anything, mock.Anything).Return(podVolumeBackupList, nil) - veleroV1.On("PodVolumeBackups", mock.Anything, mock.Anything).Return(podVolumeBackups, nil) - client.On("VeleroV1").Return(veleroV1, nil) f.On("ClientConfig").Return(&clientConfig, nil) - f.On("Client").Return(client, nil) - f.On("Namespace").Return(mock.Anything) - f.On("KubebuilderClient").Return(nil, nil) + f.On("Namespace").Return(cmdtest.VeleroNameSpace) + f.On("KubebuilderClient").Return(kbClient, nil) // create command c := NewDescribeCommand(f, "velero backup describe") @@ -74,7 +56,7 @@ func TestNewDescribeCommand(t *testing.T) { features.NewFeatureFlagSet("EnableCSI") defer features.NewFeatureFlagSet() - c.SetArgs([]string{"bk1"}) + c.SetArgs([]string{backupName}) e := c.Execute() assert.NoError(t, e) diff --git a/pkg/cmd/cli/backup/download.go b/pkg/cmd/cli/backup/download.go index dec0864e02..5d2fec546c 100644 --- a/pkg/cmd/cli/backup/download.go +++ b/pkg/cmd/cli/backup/download.go @@ -26,7 +26,7 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/pflag" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + controllerclient "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/client" @@ -84,10 +84,11 @@ func (o *DownloadOptions) BindFlags(flags *pflag.FlagSet) { } func (o *DownloadOptions) Validate(c *cobra.Command, args []string, f client.Factory) error { - veleroClient, err := f.Client() + kbClient, err := f.KubebuilderClient() cmd.CheckError(err) - if _, err := veleroClient.VeleroV1().Backups(f.Namespace()).Get(context.TODO(), o.Name, metav1.GetOptions{}); err != nil { + backup := new(velerov1api.Backup) + if err := kbClient.Get(context.TODO(), controllerclient.ObjectKey{Namespace: f.Namespace(), Name: o.Name}, backup); err != nil { return err } diff --git a/pkg/cmd/cli/backup/download_test.go b/pkg/cmd/cli/backup/download_test.go index 02bb4ba30d..3a28b73f76 100644 --- a/pkg/cmd/cli/backup/download_test.go +++ b/pkg/cmd/cli/backup/download_test.go @@ -17,6 +17,7 @@ limitations under the License. package backup import ( + "context" "fmt" "os" "os/exec" @@ -25,35 +26,29 @@ import ( flag "github.com/spf13/pflag" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" "sigs.k8s.io/controller-runtime/pkg/client/fake" + "github.com/vmware-tanzu/velero/pkg/builder" cmdtest "github.com/vmware-tanzu/velero/pkg/cmd/test" "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks" - versionedmocks "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/mocks" - velerov1mocks "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1/mocks" ) func TestNewDownloadCommand(t *testing.T) { - // create a factory f := &factorymocks.Factory{} - backups := &velerov1mocks.BackupInterface{} - veleroV1 := &velerov1mocks.VeleroV1Interface{} - client := &versionedmocks.Interface{} - bk := &velerov1api.Backup{} + backupName := "backup-1" kbclient := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build() + err := kbclient.Create(context.Background(), builder.ForBackup(cmdtest.VeleroNameSpace, backupName).Result()) + require.NoError(t, err) + err = kbclient.Create(context.Background(), builder.ForBackup(cmdtest.VeleroNameSpace, "bk-to-be-download").Result()) + require.NoError(t, err) - backups.On("Get", 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) - f.On("Namespace").Return(mock.Anything) + f.On("Namespace").Return(cmdtest.VeleroNameSpace) f.On("KubebuilderClient").Return(kbclient, nil) // create command @@ -78,7 +73,6 @@ func TestNewDownloadCommand(t *testing.T) { flags.Parse([]string{fmt.Sprintf("--insecure-skip-tls-verify=%s", strconv.FormatBool(insecureSkipTlsVerify))}) flags.Parse([]string{"--cacert", cacert}) - backupName := "backup-1" args := []string{backupName, "arg2"} e := o.Complete(args) @@ -105,7 +99,7 @@ func TestNewDownloadCommand(t *testing.T) { _, stderr, err := veleroexec.RunCommand(cmd) if err != nil { - assert.Contains(t, stderr, "download request download url timeout") + require.Contains(t, stderr, "download request download url timeout") return } t.Fatalf("process ran with err %v, want backup delete successfully", err) diff --git a/pkg/cmd/cli/backuplocation/delete_test.go b/pkg/cmd/cli/backuplocation/delete_test.go index 4fe9dbc1aa..9736c9883a 100644 --- a/pkg/cmd/cli/backuplocation/delete_test.go +++ b/pkg/cmd/cli/backuplocation/delete_test.go @@ -26,14 +26,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks" "github.com/vmware-tanzu/velero/pkg/cmd/cli" cmdtest "github.com/vmware-tanzu/velero/pkg/cmd/test" - versionedmocks "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/mocks" - "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" + velerotest "github.com/vmware-tanzu/velero/pkg/test" veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" ) @@ -41,9 +38,7 @@ func TestNewDeleteCommand(t *testing.T) { // create a factory f := &factorymocks.Factory{} - client := &versionedmocks.Interface{} - kbclient := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build() - f.On("Client").Return(client, nil) + kbclient := velerotest.NewFakeControllerRuntimeClient(t) f.On("Namespace").Return(mock.Anything) f.On("KubebuilderClient").Return(kbclient, nil) @@ -86,9 +81,7 @@ func TestDeleteFunctions(t *testing.T) { //t.Run("create the other create command with fromSchedule option for Run() other branches", func(t *testing.T) { // create a factory f := &factorymocks.Factory{} - client := &versionedmocks.Interface{} - kbclient := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build() - f.On("Client").Return(client, nil) + kbclient := velerotest.NewFakeControllerRuntimeClient(t) f.On("Namespace").Return(mock.Anything) f.On("KubebuilderClient").Return(kbclient, nil) diff --git a/pkg/cmd/cli/backuplocation/get_test.go b/pkg/cmd/cli/backuplocation/get_test.go index 34781c1b56..2e4c5510cb 100644 --- a/pkg/cmd/cli/backuplocation/get_test.go +++ b/pkg/cmd/cli/backuplocation/get_test.go @@ -24,11 +24,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "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/scheme" + velerotest "github.com/vmware-tanzu/velero/pkg/test" veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" ) @@ -36,7 +35,7 @@ func TestNewGetCommand(t *testing.T) { bkList := []string{"b1", "b2"} f := &factorymocks.Factory{} - kbclient := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build() + kbclient := velerotest.NewFakeControllerRuntimeClient(t) f.On("Namespace").Return(mock.Anything) f.On("KubebuilderClient").Return(kbclient, nil) diff --git a/pkg/cmd/cli/delete_options.go b/pkg/cmd/cli/delete_options.go index c6197a428c..1a5d711dc5 100644 --- a/pkg/cmd/cli/delete_options.go +++ b/pkg/cmd/cli/delete_options.go @@ -25,16 +25,16 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" + controllerclient "sigs.k8s.io/controller-runtime/pkg/client" "github.com/vmware-tanzu/velero/pkg/client" - clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" ) // DeleteOptions contains parameters used for deleting a restore. type DeleteOptions struct { *SelectOptions Confirm bool - Client clientset.Interface + Client controllerclient.Client Namespace string } @@ -47,7 +47,7 @@ func NewDeleteOptions(singularTypeName string) *DeleteOptions { // Complete fills in the correct values for all the options. func (o *DeleteOptions) Complete(f client.Factory, args []string) error { o.Namespace = f.Namespace() - client, err := f.Client() + client, err := f.KubebuilderClient() if err != nil { return err } diff --git a/pkg/cmd/cli/restore/delete.go b/pkg/cmd/cli/restore/delete.go index 42479ced28..6bc4eec65b 100644 --- a/pkg/cmd/cli/restore/delete.go +++ b/pkg/cmd/cli/restore/delete.go @@ -25,6 +25,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" kubeerrs "k8s.io/apimachinery/pkg/util/errors" + controllerclient "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/client" @@ -76,7 +77,8 @@ func Run(o *cli.DeleteOptions) error { switch { case len(o.Names) > 0: for _, name := range o.Names { - restore, err := o.Client.VeleroV1().Restores(o.Namespace).Get(context.TODO(), name, metav1.GetOptions{}) + restore := new(velerov1api.Restore) + err := o.Client.Get(context.TODO(), controllerclient.ObjectKey{Namespace: o.Namespace, Name: name}, restore) if err != nil { errs = append(errs, errors.WithStack(err)) continue @@ -84,27 +86,38 @@ func Run(o *cli.DeleteOptions) error { restores = append(restores, restore) } default: - selector := labels.Everything().String() + selector := labels.Everything() if o.Selector.LabelSelector != nil { - selector = o.Selector.String() + convertedSelector, err := metav1.LabelSelectorAsSelector(o.Selector.LabelSelector) + if err != nil { + return errors.WithStack(err) + } + selector = convertedSelector } - res, err := o.Client.VeleroV1().Restores(o.Namespace).List(context.TODO(), metav1.ListOptions{ + restoreList := new(velerov1api.RestoreList) + err := o.Client.List(context.TODO(), restoreList, &controllerclient.ListOptions{ LabelSelector: selector, }) if err != nil { errs = append(errs, errors.WithStack(err)) } - for i := range res.Items { - restores = append(restores, &res.Items[i]) + for i := range restoreList.Items { + restores = append(restores, &restoreList.Items[i]) } } + + if len(errs) > 0 { + fmt.Println("errs: ", errs) + return kubeerrs.NewAggregate(errs) + } + if len(restores) == 0 { fmt.Println("No restores found") return nil } for _, r := range restores { - err := o.Client.VeleroV1().Restores(r.Namespace).Delete(context.TODO(), r.Name, metav1.DeleteOptions{}) + err := o.Client.Delete(context.TODO(), r, &controllerclient.DeleteOptions{}) if err != nil { errs = append(errs, errors.WithStack(err)) continue diff --git a/pkg/cmd/cli/restore/describe.go b/pkg/cmd/cli/restore/describe.go index 88514e8651..10aca7e025 100644 --- a/pkg/cmd/cli/restore/describe.go +++ b/pkg/cmd/cli/restore/describe.go @@ -75,7 +75,7 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { fmt.Fprintf(os.Stderr, "error getting PodVolumeRestores for restore %s: %v\n", restore.Name, err) } - s := output.DescribeRestore(context.Background(), kbClient, &restores.Items[i], podvolumeRestoreList.Items, details, veleroClient, insecureSkipTLSVerify, caCertFile) + s := output.DescribeRestore(context.Background(), kbClient, &restores.Items[i], podvolumeRestoreList.Items, details, insecureSkipTLSVerify, caCertFile) if first { first = false fmt.Print(s) diff --git a/pkg/cmd/cli/schedule/delete.go b/pkg/cmd/cli/schedule/delete.go index af7c7c28d4..67cecffe6e 100644 --- a/pkg/cmd/cli/schedule/delete.go +++ b/pkg/cmd/cli/schedule/delete.go @@ -25,6 +25,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" kubeerrs "k8s.io/apimachinery/pkg/util/errors" + controllerclient "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/client" @@ -76,7 +77,8 @@ func Run(o *cli.DeleteOptions) error { switch { case len(o.Names) > 0: for _, name := range o.Names { - schedule, err := o.Client.VeleroV1().Schedules(o.Namespace).Get(context.TODO(), name, metav1.GetOptions{}) + schedule := new(velerov1api.Schedule) + err := o.Client.Get(context.TODO(), controllerclient.ObjectKey{Namespace: o.Namespace, Name: name}, schedule) if err != nil { errs = append(errs, errors.WithStack(err)) continue @@ -84,19 +86,24 @@ func Run(o *cli.DeleteOptions) error { schedules = append(schedules, schedule) } default: - selector := labels.Everything().String() + selector := labels.Everything() if o.Selector.LabelSelector != nil { - selector = o.Selector.String() + convertedSelector, err := metav1.LabelSelectorAsSelector(o.Selector.LabelSelector) + if err != nil { + return errors.WithStack(err) + } + selector = convertedSelector } - res, err := o.Client.VeleroV1().Schedules(o.Namespace).List(context.TODO(), metav1.ListOptions{ + scheduleList := new(velerov1api.ScheduleList) + err := o.Client.List(context.TODO(), scheduleList, &controllerclient.ListOptions{ LabelSelector: selector, }) if err != nil { errs = append(errs, errors.WithStack(err)) } - for i := range res.Items { - schedules = append(schedules, &res.Items[i]) + for i := range scheduleList.Items { + schedules = append(schedules, &scheduleList.Items[i]) } } if len(schedules) == 0 { @@ -105,7 +112,7 @@ func Run(o *cli.DeleteOptions) error { } for _, s := range schedules { - err := o.Client.VeleroV1().Schedules(s.Namespace).Delete(context.TODO(), s.Name, metav1.DeleteOptions{}) + err := o.Client.Delete(context.TODO(), s, &controllerclient.DeleteOptions{}) if err != nil { errs = append(errs, errors.WithStack(err)) continue diff --git a/pkg/cmd/cli/version/version_test.go b/pkg/cmd/cli/version/version_test.go index 90e6e811b9..355626802f 100644 --- a/pkg/cmd/cli/version/version_test.go +++ b/pkg/cmd/cli/version/version_test.go @@ -25,12 +25,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" kbclient "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/buildinfo" - "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" + velerotest "github.com/vmware-tanzu/velero/pkg/test" ) func TestPrintVersion(t *testing.T) { @@ -83,7 +82,7 @@ func TestPrintVersion(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { var ( - kbClient = fake.NewClientBuilder().WithScheme(scheme.Scheme).Build() + kbClient = velerotest.NewFakeControllerRuntimeClient(t) serverStatusGetter = new(mockServerStatusGetter) buf = new(bytes.Buffer) ) diff --git a/pkg/cmd/util/output/backup_describer.go b/pkg/cmd/util/output/backup_describer.go index 23ca389760..ec3878b626 100644 --- a/pkg/cmd/util/output/backup_describer.go +++ b/pkg/cmd/util/output/backup_describer.go @@ -36,7 +36,6 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/cmd/util/downloadrequest" "github.com/vmware-tanzu/velero/pkg/features" - clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" "github.com/vmware-tanzu/velero/pkg/itemoperation" "github.com/vmware-tanzu/velero/pkg/util/collections" @@ -53,7 +52,6 @@ func DescribeBackup( podVolumeBackups []velerov1api.PodVolumeBackup, volumeSnapshotContents []snapshotv1api.VolumeSnapshotContent, details bool, - veleroClient clientset.Interface, insecureSkipTLSVerify bool, caCertFile string, ) string { @@ -106,7 +104,7 @@ func DescribeBackup( DescribeBackupSpec(d, backup.Spec) d.Println() - DescribeBackupStatus(ctx, kbClient, d, backup, details, veleroClient, insecureSkipTLSVerify, caCertFile) + DescribeBackupStatus(ctx, kbClient, d, backup, details, insecureSkipTLSVerify, caCertFile) if len(deleteRequests) > 0 { d.Println() @@ -300,7 +298,7 @@ func DescribeBackupSpec(d *Describer, spec velerov1api.BackupSpec) { } // DescribeBackupStatus describes a backup status in human-readable format. -func DescribeBackupStatus(ctx context.Context, kbClient kbclient.Client, d *Describer, backup *velerov1api.Backup, details bool, veleroClient clientset.Interface, insecureSkipTLSVerify bool, caCertPath string) { +func DescribeBackupStatus(ctx context.Context, kbClient kbclient.Client, d *Describer, backup *velerov1api.Backup, details bool, insecureSkipTLSVerify bool, caCertPath string) { status := backup.Status // Status.Version has been deprecated, use Status.FormatVersion diff --git a/pkg/cmd/util/output/backup_structured_describer.go b/pkg/cmd/util/output/backup_structured_describer.go index cecab22cbc..4a69fc057c 100644 --- a/pkg/cmd/util/output/backup_structured_describer.go +++ b/pkg/cmd/util/output/backup_structured_describer.go @@ -33,7 +33,6 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/cmd/util/downloadrequest" "github.com/vmware-tanzu/velero/pkg/features" - clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" "github.com/vmware-tanzu/velero/pkg/util/results" "github.com/vmware-tanzu/velero/pkg/volume" ) @@ -47,7 +46,6 @@ func DescribeBackupInSF( podVolumeBackups []velerov1api.PodVolumeBackup, volumeSnapshotContents []snapshotv1api.VolumeSnapshotContent, details bool, - veleroClient clientset.Interface, insecureSkipTLSVerify bool, caCertFile string, outputFormat string, @@ -70,7 +68,7 @@ func DescribeBackupInSF( DescribeBackupSpecInSF(d, backup.Spec) - DescribeBackupStatusInSF(ctx, kbClient, d, backup, details, veleroClient, insecureSkipTLSVerify, caCertFile) + DescribeBackupStatusInSF(ctx, kbClient, d, backup, details, insecureSkipTLSVerify, caCertFile) if len(deleteRequests) > 0 { DescribeDeleteBackupRequestsInSF(d, deleteRequests) @@ -236,7 +234,7 @@ func DescribeBackupSpecInSF(d *StructuredDescriber, spec velerov1api.BackupSpec) } // DescribeBackupStatusInSF describes a backup status in structured format. -func DescribeBackupStatusInSF(ctx context.Context, kbClient kbclient.Client, d *StructuredDescriber, backup *velerov1api.Backup, details bool, veleroClient clientset.Interface, insecureSkipTLSVerify bool, caCertPath string) { +func DescribeBackupStatusInSF(ctx context.Context, kbClient kbclient.Client, d *StructuredDescriber, backup *velerov1api.Backup, details bool, insecureSkipTLSVerify bool, caCertPath string) { status := backup.Status backupStatusInfo := make(map[string]interface{}) diff --git a/pkg/cmd/util/output/restore_describer.go b/pkg/cmd/util/output/restore_describer.go index 8edd7c1846..13ac580b54 100644 --- a/pkg/cmd/util/output/restore_describer.go +++ b/pkg/cmd/util/output/restore_describer.go @@ -31,12 +31,11 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/cmd/util/downloadrequest" - clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" "github.com/vmware-tanzu/velero/pkg/itemoperation" "github.com/vmware-tanzu/velero/pkg/util/results" ) -func DescribeRestore(ctx context.Context, kbClient kbclient.Client, restore *velerov1api.Restore, podVolumeRestores []velerov1api.PodVolumeRestore, details bool, veleroClient clientset.Interface, insecureSkipTLSVerify bool, caCertFile string) string { +func DescribeRestore(ctx context.Context, kbClient kbclient.Client, restore *velerov1api.Restore, podVolumeRestores []velerov1api.PodVolumeRestore, details bool, insecureSkipTLSVerify bool, caCertFile string) string { return Describe(func(d *Describer) { d.DescribeMetadata(restore.ObjectMeta) diff --git a/pkg/util/kube/list_watch.go b/pkg/util/kube/list_watch.go new file mode 100644 index 0000000000..eac580a8c7 --- /dev/null +++ b/pkg/util/kube/list_watch.go @@ -0,0 +1,44 @@ +/* +Copyright 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 kube + +import ( + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +type InternalLW struct { + Client kbclient.WithWatch + Namespace string +} + +func (lw *InternalLW) Watch(options metav1.ListOptions) (watch.Interface, error) { + backupList := new(velerov1api.BackupList) + return lw.Client.Watch(context.Background(), backupList, &kbclient.ListOptions{Namespace: lw.Namespace}) +} + +func (lw *InternalLW) List(options metav1.ListOptions) (runtime.Object, error) { + backupList := new(velerov1api.BackupList) + err := lw.Client.List(context.Background(), backupList, &kbclient.ListOptions{Namespace: lw.Namespace}) + return backupList, err +}