From b059030666292eba9313f578810d9cc77efde30e Mon Sep 17 00:00:00 2001 From: "Hoang, Phuong" Date: Thu, 22 Jul 2021 00:20:37 -0700 Subject: [PATCH] Initial implementation of plugin version v2: Adding context object --- internal/delete/delete_item_action_handler.go | 7 +- pkg/backup/backup.go | 15 +- pkg/backup/backup_test.go | 35 +-- pkg/backup/item_backupper.go | 11 +- pkg/controller/backup_controller_test.go | 6 +- pkg/controller/backup_deletion_controller.go | 6 +- .../backup_deletion_controller_test.go | 6 +- pkg/persistence/object_store.go | 12 +- pkg/persistence/object_store_test.go | 6 +- pkg/plugin/clientmgmt/client_builder.go | 6 + pkg/plugin/clientmgmt/manager.go | 155 ++++++++---- ...startable_adapted_v1_backup_item_action.go | 97 +++++++ .../restartable_adapted_v1_object_store.go | 238 ++++++++++++++++++ ...tartable_adapted_v1_restore_item_action.go | 97 +++++++ .../restartable_backup_item_action.go | 27 +- .../restartable_delete_item_action.go | 24 +- .../clientmgmt/restartable_object_store.go | 92 ++++++- .../restartable_restore_item_action.go | 29 ++- .../restartable_volume_snapshotter.go | 86 ++++++- pkg/plugin/framework/backup_item_action.go | 2 +- .../framework/backup_item_action_client.go | 48 ++++ .../framework/backup_item_action_server.go | 7 +- .../framework/delete_item_action_client.go | 28 ++- .../framework/delete_item_action_server.go | 10 +- pkg/plugin/framework/object_store_client.go | 158 ++++++++++++ pkg/plugin/framework/object_store_server.go | 18 +- pkg/plugin/framework/plugin_kinds.go | 21 ++ .../framework/restore_item_action_client.go | 14 +- .../framework/restore_item_action_server.go | 20 +- pkg/plugin/framework/server.go | 127 +++++++++- .../framework/volume_snapshotter_client.go | 56 ++++- .../framework/volume_snapshotter_server.go | 20 +- pkg/plugin/mocks/manager.go | 17 +- .../v1/backup_item_actionv1.go} | 18 +- .../v2/backup_item_actionv2.go | 38 +++ pkg/plugin/velero/delete_item_action.go | 16 +- .../v1/delete_item_actionv1.go | 35 +++ .../v2/delete_item_actionv2.go | 34 +++ pkg/plugin/velero/mocks/DeleteItemAction.go | 7 +- .../v1/object_storev1.go} | 2 +- .../velero/objectstore/v2/object_storev2.go | 68 +++++ pkg/plugin/velero/restore_item_action.go | 18 +- .../v1/restore_item_actionv1.go | 37 +++ .../v2/restore_item_actionv2.go | 37 +++ pkg/plugin/velero/shared.go | 13 +- .../v1/volume_snapshotterv1.go} | 2 +- .../v2/volume_snapshotterv2.go | 59 +++++ pkg/restore/restore.go | 12 +- 48 files changed, 1652 insertions(+), 245 deletions(-) create mode 100644 pkg/plugin/clientmgmt/restartable_adapted_v1_backup_item_action.go create mode 100644 pkg/plugin/clientmgmt/restartable_adapted_v1_object_store.go create mode 100644 pkg/plugin/clientmgmt/restartable_adapted_v1_restore_item_action.go rename pkg/plugin/velero/{backup_item_action.go => backupitemaction/v1/backup_item_actionv1.go} (75%) create mode 100644 pkg/plugin/velero/backupitemaction/v2/backup_item_actionv2.go create mode 100644 pkg/plugin/velero/deleteitemaction/v1/delete_item_actionv1.go create mode 100644 pkg/plugin/velero/deleteitemaction/v2/delete_item_actionv2.go rename pkg/plugin/velero/{object_store.go => objectstore/v1/object_storev1.go} (99%) create mode 100644 pkg/plugin/velero/objectstore/v2/object_storev2.go create mode 100644 pkg/plugin/velero/restoreitemaction/v1/restore_item_actionv1.go create mode 100644 pkg/plugin/velero/restoreitemaction/v2/restore_item_actionv2.go rename pkg/plugin/velero/{volume_snapshotter.go => volumesnapshotter/v1/volume_snapshotterv1.go} (99%) create mode 100644 pkg/plugin/velero/volumesnapshotter/v2/volume_snapshotterv2.go diff --git a/internal/delete/delete_item_action_handler.go b/internal/delete/delete_item_action_handler.go index c8545c345..7c6d08dec 100644 --- a/internal/delete/delete_item_action_handler.go +++ b/internal/delete/delete_item_action_handler.go @@ -29,6 +29,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/archive" "github.com/vmware-tanzu/velero/pkg/discovery" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + deleteactionitemv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/deleteitemaction/v2" "github.com/vmware-tanzu/velero/pkg/util/collections" "github.com/vmware-tanzu/velero/pkg/util/filesystem" ) @@ -37,7 +38,7 @@ import ( type Context struct { Backup *velerov1api.Backup BackupReader io.Reader - Actions []velero.DeleteItemAction + Actions []deleteactionitemv2.DeleteItemAction Filesystem filesystem.Interface Log logrus.FieldLogger DiscoveryHelper discovery.Helper @@ -163,7 +164,7 @@ func (ctx *Context) getApplicableActions(groupResource schema.GroupResource, nam // resolvedActions are DeleteItemActions decorated with resource/namespace include/exclude collections, as well as label selectors for easy comparison. type resolvedAction struct { - velero.DeleteItemAction + deleteactionitemv2.DeleteItemAction resourceIncludesExcludes *collections.IncludesExcludes namespaceIncludesExcludes *collections.IncludesExcludes @@ -171,7 +172,7 @@ type resolvedAction struct { } // resolveActions resolves the AppliesTo ResourceSelectors of DeleteItemActions plugins against the Kubernetes discovery API for fully-qualified names. -func resolveActions(actions []velero.DeleteItemAction, helper discovery.Helper) ([]resolvedAction, error) { +func resolveActions(actions []deleteactionitemv2.DeleteItemAction, helper discovery.Helper) ([]resolvedAction, error) { var resolved []resolvedAction for _, action := range actions { diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index 746695e00..90ab29270 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -44,7 +44,8 @@ import ( "github.com/vmware-tanzu/velero/pkg/discovery" velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" "github.com/vmware-tanzu/velero/pkg/kuberesource" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + backupitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2" + volumesnapshotterv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v2" "github.com/vmware-tanzu/velero/pkg/podexec" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/collections" @@ -61,7 +62,8 @@ const BackupFormatVersion = "1.1.0" type Backupper interface { // Backup takes a backup using the specification in the velerov1api.Backup and writes backup and log data // to the given writers. - Backup(logger logrus.FieldLogger, backup *Request, backupFile io.Writer, actions []velero.BackupItemAction, volumeSnapshotterGetter VolumeSnapshotterGetter) error + Backup(logger logrus.FieldLogger, backup *Request, backupFile io.Writer, + actions []backupitemactionv2.BackupItemAction, volumeSnapshotterGetter VolumeSnapshotterGetter) error } // kubernetesBackupper implements Backupper. @@ -77,7 +79,7 @@ type kubernetesBackupper struct { } type resolvedAction struct { - velero.BackupItemAction + backupitemactionv2.BackupItemAction resourceIncludesExcludes *collections.IncludesExcludes namespaceIncludesExcludes *collections.IncludesExcludes @@ -121,7 +123,7 @@ func NewKubernetesBackupper( }, nil } -func resolveActions(actions []velero.BackupItemAction, helper discovery.Helper) ([]resolvedAction, error) { +func resolveActions(actions []backupitemactionv2.BackupItemAction, helper discovery.Helper) ([]resolvedAction, error) { var resolved []resolvedAction for _, action := range actions { @@ -197,7 +199,7 @@ func getResourceHook(hookSpec velerov1api.BackupResourceHookSpec, discoveryHelpe } type VolumeSnapshotterGetter interface { - GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) + GetVolumeSnapshotter(name string) (volumesnapshotterv2.VolumeSnapshotter, error) } // Backup backs up the items specified in the Backup, placing them in a gzip-compressed tar file @@ -205,7 +207,8 @@ type VolumeSnapshotterGetter interface { // a complete backup failure is returned. Errors that constitute partial failures (i.e. failures to // back up individual resources that don't prevent the backup from continuing to be processed) are logged // to the backup log. -func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Request, backupFile io.Writer, actions []velero.BackupItemAction, volumeSnapshotterGetter VolumeSnapshotterGetter) error { +func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Request, backupFile io.Writer, + actions []backupitemactionv2.BackupItemAction, volumeSnapshotterGetter VolumeSnapshotterGetter) error { gzippedData := gzip.NewWriter(backupFile) defer gzippedData.Close() diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index 0747409bb..19666e889 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -47,6 +47,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/discovery" "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + backupitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/test" testutil "github.com/vmware-tanzu/velero/pkg/test" @@ -1331,7 +1332,7 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) { h.addItems(t, resource) } - actions := []velero.BackupItemAction{} + actions := []backupitemactionv2.BackupItemAction{} for action := range tc.actions { actions = append(actions, action) } @@ -1357,7 +1358,7 @@ func TestBackupWithInvalidActions(t *testing.T) { name string backup *velerov1.Backup apiResources []*test.APIResource - actions []velero.BackupItemAction + actions []backupitemactionv2.BackupItemAction }{ { name: "action with invalid label selector results in an error", @@ -1373,7 +1374,7 @@ func TestBackupWithInvalidActions(t *testing.T) { builder.ForPersistentVolume("baz").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []backupitemactionv2.BackupItemAction{ new(recordResourcesAction).ForLabelSelector("=invalid-selector"), }, }, @@ -1391,7 +1392,7 @@ func TestBackupWithInvalidActions(t *testing.T) { builder.ForPersistentVolume("baz").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []backupitemactionv2.BackupItemAction{ &appliesToErrorAction{}, }, }, @@ -1453,7 +1454,7 @@ func TestBackupActionModifications(t *testing.T) { name string backup *velerov1.Backup apiResources []*test.APIResource - actions []velero.BackupItemAction + actions []backupitemactionv2.BackupItemAction want map[string]unstructuredObject }{ { @@ -1464,7 +1465,7 @@ func TestBackupActionModifications(t *testing.T) { builder.ForPod("ns-1", "pod-1").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []backupitemactionv2.BackupItemAction{ modifyingActionGetter(func(item *unstructured.Unstructured) { item.SetLabels(map[string]string{"updated": "true"}) }), @@ -1481,7 +1482,7 @@ func TestBackupActionModifications(t *testing.T) { builder.ForPod("ns-1", "pod-1").ObjectMeta(builder.WithLabels("should-be-removed", "true")).Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []backupitemactionv2.BackupItemAction{ modifyingActionGetter(func(item *unstructured.Unstructured) { item.SetLabels(nil) }), @@ -1498,7 +1499,7 @@ func TestBackupActionModifications(t *testing.T) { builder.ForPod("ns-1", "pod-1").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []backupitemactionv2.BackupItemAction{ modifyingActionGetter(func(item *unstructured.Unstructured) { item.Object["spec"].(map[string]interface{})["nodeName"] = "foo" }), @@ -1516,7 +1517,7 @@ func TestBackupActionModifications(t *testing.T) { builder.ForPod("ns-1", "pod-1").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []backupitemactionv2.BackupItemAction{ modifyingActionGetter(func(item *unstructured.Unstructured) { item.SetName(item.GetName() + "-updated") item.SetNamespace(item.GetNamespace() + "-updated") @@ -1557,7 +1558,7 @@ func TestBackupActionAdditionalItems(t *testing.T) { name string backup *velerov1.Backup apiResources []*test.APIResource - actions []velero.BackupItemAction + actions []backupitemactionv2.BackupItemAction want []string }{ { @@ -1570,7 +1571,7 @@ func TestBackupActionAdditionalItems(t *testing.T) { builder.ForPod("ns-3", "pod-3").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []backupitemactionv2.BackupItemAction{ &pluggableAction{ selector: velero.ResourceSelector{IncludedNamespaces: []string{"ns-1"}}, executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { @@ -1602,7 +1603,7 @@ func TestBackupActionAdditionalItems(t *testing.T) { builder.ForPod("ns-3", "pod-3").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []backupitemactionv2.BackupItemAction{ &pluggableAction{ executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { additionalItems := []velero.ResourceIdentifier{ @@ -1632,7 +1633,7 @@ func TestBackupActionAdditionalItems(t *testing.T) { builder.ForPersistentVolume("pv-2").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []backupitemactionv2.BackupItemAction{ &pluggableAction{ executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { additionalItems := []velero.ResourceIdentifier{ @@ -1665,7 +1666,7 @@ func TestBackupActionAdditionalItems(t *testing.T) { builder.ForPersistentVolume("pv-2").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []backupitemactionv2.BackupItemAction{ &pluggableAction{ executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { additionalItems := []velero.ResourceIdentifier{ @@ -1695,7 +1696,7 @@ func TestBackupActionAdditionalItems(t *testing.T) { builder.ForPersistentVolume("pv-2").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []backupitemactionv2.BackupItemAction{ &pluggableAction{ executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { additionalItems := []velero.ResourceIdentifier{ @@ -1726,7 +1727,7 @@ func TestBackupActionAdditionalItems(t *testing.T) { builder.ForPersistentVolume("pv-2").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []backupitemactionv2.BackupItemAction{ &pluggableAction{ executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { additionalItems := []velero.ResourceIdentifier{ @@ -1756,7 +1757,7 @@ func TestBackupActionAdditionalItems(t *testing.T) { builder.ForPod("ns-3", "pod-3").Result(), ), }, - actions: []velero.BackupItemAction{ + actions: []backupitemactionv2.BackupItemAction{ &pluggableAction{ selector: velero.ResourceSelector{IncludedNamespaces: []string{"ns-1"}}, executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index 4f5c7ca8b..a50da254f 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -39,7 +39,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/discovery" "github.com/vmware-tanzu/velero/pkg/kuberesource" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + volumesnapshotterv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v2" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/volume" @@ -56,7 +56,7 @@ type itemBackupper struct { volumeSnapshotterGetter VolumeSnapshotterGetter itemHookHandler hook.ItemHookHandler - snapshotLocationVolumeSnapshotters map[string]velero.VolumeSnapshotter + snapshotLocationVolumeSnapshotters map[string]volumesnapshotterv2.VolumeSnapshotter } // backupItem backs up an individual item to tarWriter. The item may be excluded based on the @@ -367,7 +367,8 @@ func (ib *itemBackupper) executeActions( // volumeSnapshotter instantiates and initializes a VolumeSnapshotter given a VolumeSnapshotLocation, // or returns an existing one if one's already been initialized for the location. -func (ib *itemBackupper) volumeSnapshotter(snapshotLocation *velerov1api.VolumeSnapshotLocation) (velero.VolumeSnapshotter, error) { +func (ib *itemBackupper) volumeSnapshotter(snapshotLocation *velerov1api.VolumeSnapshotLocation) ( + volumesnapshotterv2.VolumeSnapshotter, error) { if bs, ok := ib.snapshotLocationVolumeSnapshotters[snapshotLocation.Name]; ok { return bs, nil } @@ -382,7 +383,7 @@ func (ib *itemBackupper) volumeSnapshotter(snapshotLocation *velerov1api.VolumeS } if ib.snapshotLocationVolumeSnapshotters == nil { - ib.snapshotLocationVolumeSnapshotters = make(map[string]velero.VolumeSnapshotter) + ib.snapshotLocationVolumeSnapshotters = make(map[string]volumesnapshotterv2.VolumeSnapshotter) } ib.snapshotLocationVolumeSnapshotters[snapshotLocation.Name] = bs @@ -438,7 +439,7 @@ func (ib *itemBackupper) takePVSnapshot(obj runtime.Unstructured, log logrus.Fie var ( volumeID, location string - volumeSnapshotter velero.VolumeSnapshotter + volumeSnapshotter volumesnapshotterv2.VolumeSnapshotter ) for _, snapshotLocation := range ib.backupRequest.SnapshotLocations { diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 16fc6c1a0..32e99686d 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -48,7 +48,7 @@ import ( persistencemocks "github.com/vmware-tanzu/velero/pkg/persistence/mocks" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" pluginmocks "github.com/vmware-tanzu/velero/pkg/plugin/mocks" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + backupitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2" velerotest "github.com/vmware-tanzu/velero/pkg/test" "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/util/logging" @@ -58,7 +58,7 @@ type fakeBackupper struct { mock.Mock } -func (b *fakeBackupper) Backup(logger logrus.FieldLogger, backup *pkgbackup.Request, backupFile io.Writer, actions []velero.BackupItemAction, volumeSnapshotterGetter pkgbackup.VolumeSnapshotterGetter) error { +func (b *fakeBackupper) Backup(logger logrus.FieldLogger, backup *pkgbackup.Request, backupFile io.Writer, actions []backupitemactionv2.BackupItemAction, volumeSnapshotterGetter pkgbackup.VolumeSnapshotterGetter) error { args := b.Called(logger, backup, backupFile, actions, volumeSnapshotterGetter) return args.Error(0) } @@ -825,7 +825,7 @@ func TestProcessBackupCompletions(t *testing.T) { pluginManager.On("GetBackupItemActions").Return(nil, nil) pluginManager.On("CleanupClients").Return(nil) - backupper.On("Backup", mock.Anything, mock.Anything, mock.Anything, []velero.BackupItemAction(nil), pluginManager).Return(nil) + backupper.On("Backup", mock.Anything, mock.Anything, mock.Anything, []backupitemactionv2.BackupItemAction(nil), pluginManager).Return(nil) backupStore.On("BackupExists", test.backupLocation.Spec.StorageType.ObjectStorage.Bucket, test.backup.Name).Return(test.backupExists, test.existenceCheckError) // Ensure we have a CompletionTimestamp when uploading and that the backup name matches the backup in the object store. diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 3e503e81a..0fc13013e 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -46,7 +46,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/metrics" "github.com/vmware-tanzu/velero/pkg/persistence" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + volumesnapshotter "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v2" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/kube" @@ -333,7 +333,7 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR if snapshots, err := backupStore.GetBackupVolumeSnapshots(backup.Name); err != nil { errs = append(errs, errors.Wrap(err, "error getting backup's volume snapshots").Error()) } else { - volumeSnapshotters := make(map[string]velero.VolumeSnapshotter) + volumeSnapshotters := make(map[string]volumesnapshotter.VolumeSnapshotter) for _, snapshot := range snapshots { log.WithField("providerSnapshotID", snapshot.Status.ProviderSnapshotID).Info("Removing snapshot associated with backup") @@ -433,7 +433,7 @@ func volumeSnapshotterForSnapshotLocation( namespace, snapshotLocationName string, snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister, pluginManager clientmgmt.Manager, -) (velero.VolumeSnapshotter, error) { +) (volumesnapshotter.VolumeSnapshotter, error) { snapshotLocation, err := snapshotLocationLister.VolumeSnapshotLocations(namespace).Get(snapshotLocationName) if err != nil { return nil, errors.Wrapf(err, "error getting volume snapshot location %s", snapshotLocationName) diff --git a/pkg/controller/backup_deletion_controller_test.go b/pkg/controller/backup_deletion_controller_test.go index 98109d7fe..76e1e05ed 100644 --- a/pkg/controller/backup_deletion_controller_test.go +++ b/pkg/controller/backup_deletion_controller_test.go @@ -45,7 +45,7 @@ import ( persistencemocks "github.com/vmware-tanzu/velero/pkg/persistence/mocks" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" pluginmocks "github.com/vmware-tanzu/velero/pkg/plugin/mocks" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + deleteitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/deleteitemaction/v2" "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks" velerotest "github.com/vmware-tanzu/velero/pkg/test" "github.com/vmware-tanzu/velero/pkg/volume" @@ -802,7 +802,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { pluginManager := &pluginmocks.Manager{} pluginManager.On("GetVolumeSnapshotter", "provider-1").Return(td.volumeSnapshotter, nil) - pluginManager.On("GetDeleteItemActions").Return([]velero.DeleteItemAction{}, nil) + pluginManager.On("GetDeleteItemActions").Return([]deleteitemactionv2.DeleteItemAction{}, nil) pluginManager.On("CleanupClients") td.controller.newPluginManager = func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager } @@ -932,7 +932,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { pluginManager := &pluginmocks.Manager{} pluginManager.On("GetVolumeSnapshotter", "provider-1").Return(td.volumeSnapshotter, nil) - pluginManager.On("GetDeleteItemActions").Return([]velero.DeleteItemAction{new(mocks.DeleteItemAction)}, nil) + pluginManager.On("GetDeleteItemActions").Return([]deleteitemactionv2.DeleteItemAction{new(mocks.DeleteItemAction)}, nil) pluginManager.On("CleanupClients") td.controller.newPluginManager = func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager } diff --git a/pkg/persistence/object_store.go b/pkg/persistence/object_store.go index d9f2d8a39..2d999a376 100644 --- a/pkg/persistence/object_store.go +++ b/pkg/persistence/object_store.go @@ -33,7 +33,7 @@ import ( "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + objectstorev2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/objectstore/v2" "github.com/vmware-tanzu/velero/pkg/volume" ) @@ -80,16 +80,16 @@ type BackupStore interface { const DownloadURLTTL = 10 * time.Minute type objectBackupStore struct { - objectStore velero.ObjectStore + objectStore objectstorev2.ObjectStore bucket string layout *ObjectStoreLayout logger logrus.FieldLogger } -// ObjectStoreGetter is a type that can get a velero.ObjectStore +// ObjectStoreGetter is a type that can get a objectstorev2.ObjectStore // from a provider name. type ObjectStoreGetter interface { - GetObjectStore(provider string) (velero.ObjectStore, error) + GetObjectStore(provider string) (objectstorev2.ObjectStore, error) } // ObjectBackupStoreGetter is a type that can get a velero.BackupStore for a @@ -326,7 +326,7 @@ func (s *objectBackupStore) GetBackupVolumeSnapshots(name string) ([]*volume.Sna // tryGet returns the object with the given key if it exists, nil if it does not exist, // or an error if it was unable to check existence or get the object. -func tryGet(objectStore velero.ObjectStore, bucket, key string) (io.ReadCloser, error) { +func tryGet(objectStore objectstorev2.ObjectStore, bucket, key string) (io.ReadCloser, error) { exists, err := objectStore.ObjectExists(bucket, key) if err != nil { return nil, errors.WithStack(err) @@ -494,7 +494,7 @@ func seekToBeginning(r io.Reader) error { return err } -func seekAndPutObject(objectStore velero.ObjectStore, bucket, key string, file io.Reader) error { +func seekAndPutObject(objectStore objectstorev2.ObjectStore, bucket, key string, file io.Reader) error { if file == nil { return nil } diff --git a/pkg/persistence/object_store_test.go b/pkg/persistence/object_store_test.go index 76d37bd40..6eeda3162 100644 --- a/pkg/persistence/object_store_test.go +++ b/pkg/persistence/object_store_test.go @@ -36,8 +36,8 @@ import ( "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" providermocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks" + objectstorev2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/objectstore/v2" velerotest "github.com/vmware-tanzu/velero/pkg/test" "github.com/vmware-tanzu/velero/pkg/util/encode" "github.com/vmware-tanzu/velero/pkg/volume" @@ -595,9 +595,9 @@ func TestGetDownloadURL(t *testing.T) { } } -type objectStoreGetter map[string]velero.ObjectStore +type objectStoreGetter map[string]objectstorev2.ObjectStore -func (osg objectStoreGetter) GetObjectStore(provider string) (velero.ObjectStore, error) { +func (osg objectStoreGetter) GetObjectStore(provider string) (objectstorev2.ObjectStore, error) { res, ok := osg[provider] if !ok { return nil, errors.New("object store not found") diff --git a/pkg/plugin/clientmgmt/client_builder.go b/pkg/plugin/clientmgmt/client_builder.go index 60d278727..769487417 100644 --- a/pkg/plugin/clientmgmt/client_builder.go +++ b/pkg/plugin/clientmgmt/client_builder.go @@ -73,6 +73,12 @@ func (b *clientBuilder) clientConfig() *hcplugin.ClientConfig { string(framework.PluginKindPluginLister): &framework.PluginListerPlugin{}, string(framework.PluginKindRestoreItemAction): framework.NewRestoreItemActionPlugin(framework.ClientLogger(b.clientLogger)), string(framework.PluginKindDeleteItemAction): framework.NewDeleteItemActionPlugin(framework.ClientLogger(b.clientLogger)), + // Version 2 + string(framework.PluginKindBackupItemActionV2): framework.NewBackupItemActionPlugin(framework.ClientLogger(b.clientLogger)), + string(framework.PluginKindVolumeSnapshotterV2): framework.NewVolumeSnapshotterPlugin(framework.ClientLogger(b.clientLogger)), + string(framework.PluginKindObjectStoreV2): framework.NewObjectStorePlugin(framework.ClientLogger(b.clientLogger)), + string(framework.PluginKindRestoreItemActionV2): framework.NewRestoreItemActionPlugin(framework.ClientLogger(b.clientLogger)), + string(framework.PluginKindDeleteItemActionV2): framework.NewDeleteItemActionPlugin(framework.ClientLogger(b.clientLogger)), }, Logger: b.pluginLogger, Cmd: exec.Command(b.commandName, b.commandArgs...), diff --git a/pkg/plugin/clientmgmt/manager.go b/pkg/plugin/clientmgmt/manager.go index 411b3b0bd..ca5de4003 100644 --- a/pkg/plugin/clientmgmt/manager.go +++ b/pkg/plugin/clientmgmt/manager.go @@ -1,5 +1,5 @@ /* -Copyright 2020 the Velero contributors. +Copyright 2021 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. @@ -17,40 +17,44 @@ limitations under the License. package clientmgmt import ( + "errors" "strings" "sync" "github.com/sirupsen/logrus" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + backupitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2" + deleteitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/deleteitemaction/v2" + objectstorev2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/objectstore/v2" + restoreitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2" + volumesnapshotterv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v2" ) // Manager manages the lifecycles of plugins. type Manager interface { // GetObjectStore returns the ObjectStore plugin for name. - GetObjectStore(name string) (velero.ObjectStore, error) + GetObjectStore(name string) (objectstorev2.ObjectStore, error) // GetVolumeSnapshotter returns the VolumeSnapshotter plugin for name. - GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) + GetVolumeSnapshotter(name string) (volumesnapshotterv2.VolumeSnapshotter, error) // GetBackupItemActions returns all backup item action plugins. - GetBackupItemActions() ([]velero.BackupItemAction, error) + GetBackupItemActions() ([]backupitemactionv2.BackupItemAction, error) // GetBackupItemAction returns the backup item action plugin for name. - GetBackupItemAction(name string) (velero.BackupItemAction, error) + GetBackupItemAction(name string) (backupitemactionv2.BackupItemAction, error) // GetRestoreItemActions returns all restore item action plugins. - GetRestoreItemActions() ([]velero.RestoreItemAction, error) + GetRestoreItemActions() ([]restoreitemactionv2.RestoreItemAction, error) // GetRestoreItemAction returns the restore item action plugin for name. - GetRestoreItemAction(name string) (velero.RestoreItemAction, error) + GetRestoreItemAction(name string) (restoreitemactionv2.RestoreItemAction, error) // GetDeleteItemActions returns all delete item action plugins. - GetDeleteItemActions() ([]velero.DeleteItemAction, error) + GetDeleteItemActions() ([]deleteitemactionv2.DeleteItemAction, error) // GetDeleteItemAction returns the delete item action plugin for name. - GetDeleteItemAction(name string) (velero.DeleteItemAction, error) + GetDeleteItemAction(name string) (deleteitemactionv2.DeleteItemAction, error) // CleanupClients terminates all of the Manager's running plugin processes. CleanupClients() @@ -92,7 +96,7 @@ func (m *manager) CleanupClients() { m.lock.Unlock() } -// getRestartableProcess returns a restartableProcess for a plugin identified by kind and name, creating a +// getRestartableProcessV2 returns a restartableProcess for a plugin identified by kind and name, creating a // restartableProcess if it is the first time it has been requested. func (m *manager) getRestartableProcess(kind framework.PluginKind, name string) (RestartableProcess, error) { m.lock.Lock() @@ -130,38 +134,61 @@ func (m *manager) getRestartableProcess(kind framework.PluginKind, name string) } // GetObjectStore returns a restartableObjectStore for name. -func (m *manager) GetObjectStore(name string) (velero.ObjectStore, error) { +func (m *manager) GetObjectStore(name string) (objectstorev2.ObjectStore, error) { name = sanitizeName(name) - restartableProcess, err := m.getRestartableProcess(framework.PluginKindObjectStore, name) + restartableProcess, err := m.getRestartableProcess(framework.PluginKindObjectStoreV2, name) if err != nil { - return nil, err + // Check if plugin was not found + if errors.Is(err, &pluginNotFoundError{}) { + // Try again but with previous version + restartableProcess, err := m.getRestartableProcess(framework.PluginKindObjectStore, name) + if err != nil { + // No v1 version found, return + return nil, err + } + // Adapt v1 plugin to v2 + return newAdaptedV1ObjectStore(name, restartableProcess), nil + } else { + return nil, err + } } - - r := newRestartableObjectStore(name, restartableProcess) - - return r, nil + return newRestartableObjectStoreV2(name, restartableProcess), nil } // GetVolumeSnapshotter returns a restartableVolumeSnapshotter for name. -func (m *manager) GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) { +func (m *manager) GetVolumeSnapshotter(name string) (volumesnapshotterv2.VolumeSnapshotter, error) { name = sanitizeName(name) - restartableProcess, err := m.getRestartableProcess(framework.PluginKindVolumeSnapshotter, name) + restartableProcess, err := m.getRestartableProcess(framework.PluginKindVolumeSnapshotterV2, name) if err != nil { - return nil, err + // Check if plugin was not found + if errors.Is(err, &pluginNotFoundError{}) { + // Try again but with previous version + restartableProcess, err := m.getRestartableProcess(framework.PluginKindVolumeSnapshotter, name) + if err != nil { + // No v1 version found, return + return nil, err + } + // Adapt v1 plugin to v2 + return newAdaptedV1VolumeSnapshotter(name, restartableProcess), nil + } else { + return nil, err + } } - r := newRestartableVolumeSnapshotter(name, restartableProcess) + r := newRestartableVolumeSnapshotterV2(name, restartableProcess) return r, nil } // GetBackupItemActions returns all backup item actions as restartableBackupItemActions. -func (m *manager) GetBackupItemActions() ([]velero.BackupItemAction, error) { - list := m.registry.List(framework.PluginKindBackupItemAction) +func (m *manager) GetBackupItemActions() ([]backupitemactionv2.BackupItemAction, error) { + listv1 := m.registry.List(framework.PluginKindBackupItemAction) + listv2 := m.registry.List(framework.PluginKindBackupItemActionV2) + list := append(listv1, listv2...) - actions := make([]velero.BackupItemAction, 0, len(list)) + actions := make([]backupitemactionv2.BackupItemAction, 0, len(list)) for i := range list { id := list[i] @@ -178,23 +205,37 @@ func (m *manager) GetBackupItemActions() ([]velero.BackupItemAction, error) { } // GetBackupItemAction returns a restartableBackupItemAction for name. -func (m *manager) GetBackupItemAction(name string) (velero.BackupItemAction, error) { +func (m *manager) GetBackupItemAction(name string) (backupitemactionv2.BackupItemAction, error) { name = sanitizeName(name) - restartableProcess, err := m.getRestartableProcess(framework.PluginKindBackupItemAction, name) + restartableProcess, err := m.getRestartableProcess(framework.PluginKindBackupItemActionV2, name) if err != nil { - return nil, err + // Check if plugin was not found + if errors.Is(err, &pluginNotFoundError{}) { + // Try again but with previous version + restartableProcess, err := m.getRestartableProcess(framework.PluginKindBackupItemAction, name) + if err != nil { + // No v1 version found, return + return nil, err + } + // Adapt v1 plugin to v2 + return newAdaptedV1BackupItemAction(name, restartableProcess), nil + } else { + return nil, err + } } - r := newRestartableBackupItemAction(name, restartableProcess) + r := newRestartableBackupItemActionV2(name, restartableProcess) return r, nil } // GetRestoreItemActions returns all restore item actions as restartableRestoreItemActions. -func (m *manager) GetRestoreItemActions() ([]velero.RestoreItemAction, error) { - list := m.registry.List(framework.PluginKindRestoreItemAction) +func (m *manager) GetRestoreItemActions() ([]restoreitemactionv2.RestoreItemAction, error) { + listv1 := m.registry.List(framework.PluginKindRestoreItemAction) + listv2 := m.registry.List(framework.PluginKindRestoreItemActionV2) + list := append(listv1, listv2...) - actions := make([]velero.RestoreItemAction, 0, len(list)) + actions := make([]restoreitemactionv2.RestoreItemAction, 0, len(list)) for i := range list { id := list[i] @@ -211,23 +252,37 @@ func (m *manager) GetRestoreItemActions() ([]velero.RestoreItemAction, error) { } // GetRestoreItemAction returns a restartableRestoreItemAction for name. -func (m *manager) GetRestoreItemAction(name string) (velero.RestoreItemAction, error) { +func (m *manager) GetRestoreItemAction(name string) (restoreitemactionv2.RestoreItemAction, error) { name = sanitizeName(name) - restartableProcess, err := m.getRestartableProcess(framework.PluginKindRestoreItemAction, name) + restartableProcess, err := m.getRestartableProcess(framework.PluginKindRestoreItemActionV2, name) if err != nil { - return nil, err + // Check if plugin was not found + if errors.Is(err, &pluginNotFoundError{}) { + // Try again but with previous version + restartableProcess, err := m.getRestartableProcess(framework.PluginKindRestoreItemAction, name) + if err != nil { + // No v1 version found, return + return nil, err + } + // Adapt v1 plugin to v2 + return newAdaptedV1RestoreItemAction(name, restartableProcess), nil + } else { + return nil, err + } } - r := newRestartableRestoreItemAction(name, restartableProcess) + r := newRestartableRestoreItemActionV2(name, restartableProcess) return r, nil } // GetDeleteItemActions returns all delete item actions as restartableDeleteItemActions. -func (m *manager) GetDeleteItemActions() ([]velero.DeleteItemAction, error) { - list := m.registry.List(framework.PluginKindDeleteItemAction) +func (m *manager) GetDeleteItemActions() ([]deleteitemactionv2.DeleteItemAction, error) { + listv1 := m.registry.List(framework.PluginKindDeleteItemAction) + listv2 := m.registry.List(framework.PluginKindDeleteItemActionV2) + list := append(listv1, listv2...) - actions := make([]velero.DeleteItemAction, 0, len(list)) + actions := make([]deleteitemactionv2.DeleteItemAction, 0, len(list)) for i := range list { id := list[i] @@ -244,15 +299,27 @@ func (m *manager) GetDeleteItemActions() ([]velero.DeleteItemAction, error) { } // GetDeleteItemAction returns a restartableDeleteItemAction for name. -func (m *manager) GetDeleteItemAction(name string) (velero.DeleteItemAction, error) { +func (m *manager) GetDeleteItemAction(name string) (deleteitemactionv2.DeleteItemAction, error) { name = sanitizeName(name) - restartableProcess, err := m.getRestartableProcess(framework.PluginKindDeleteItemAction, name) + restartableProcess, err := m.getRestartableProcess(framework.PluginKindDeleteItemActionV2, name) if err != nil { - return nil, err + // Check if plugin was not found + if errors.Is(err, &pluginNotFoundError{}) { + // Try again but with previous version + restartableProcess, err := m.getRestartableProcess(framework.PluginKindDeleteItemAction, name) + if err != nil { + // No v1 version found, return + return nil, err + } + // Adapt v1 plugin to v2 + return newAdaptedV1DeleteItemAction(name, restartableProcess), nil + } else { + return nil, err + } } - r := newRestartableDeleteItemAction(name, restartableProcess) + r := newRestartableDeleteItemActionV2(name, restartableProcess) return r, nil } diff --git a/pkg/plugin/clientmgmt/restartable_adapted_v1_backup_item_action.go b/pkg/plugin/clientmgmt/restartable_adapted_v1_backup_item_action.go new file mode 100644 index 000000000..bacf7f5e6 --- /dev/null +++ b/pkg/plugin/clientmgmt/restartable_adapted_v1_backup_item_action.go @@ -0,0 +1,97 @@ +/* +Copyright 2018, 2021 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 clientmgmt + +import ( + "context" + + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/runtime" + api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" + backupitemactionv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" +) + +type restartableAdaptedV1BackupItemAction struct { + key kindAndName + sharedPluginProcess RestartableProcess +} + +// newAdaptedV1BackupItemAction returns a new restartableAdaptedV1BackupItemAction. +func newAdaptedV1BackupItemAction(name string, sharedPluginProcess RestartableProcess) *restartableAdaptedV1BackupItemAction { + r := &restartableAdaptedV1BackupItemAction{ + key: kindAndName{kind: framework.PluginKindBackupItemAction, name: name}, + sharedPluginProcess: sharedPluginProcess, + } + return r +} + +// getBackupItemAction returns the backup item action for this restartableAdaptedV1BackupItemAction. It does *not* restart the +// plugin process. +func (r *restartableAdaptedV1BackupItemAction) getBackupItemAction() (backupitemactionv1.BackupItemAction, error) { + plugin, err := r.sharedPluginProcess.getByKindAndName(r.key) + if err != nil { + return nil, err + } + + backupItemAction, ok := plugin.(backupitemactionv1.BackupItemAction) + if !ok { + return nil, errors.Errorf("%T is not a BackupItemAction!", plugin) + } + + return backupItemAction, nil +} + +// getDelegate restarts the plugin process (if needed) and returns the backup item action for this restartableAdaptedV1BackupItemAction. +func (r *restartableAdaptedV1BackupItemAction) getDelegate() (backupitemactionv1.BackupItemAction, error) { + if err := r.sharedPluginProcess.resetIfNeeded(); err != nil { + return nil, err + } + + return r.getBackupItemAction() +} + +// AppliesTo restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1BackupItemAction) AppliesTo() (velero.ResourceSelector, error) { + delegate, err := r.getDelegate() + if err != nil { + return velero.ResourceSelector{}, err + } + + return delegate.AppliesTo() +} + +// Execute restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1BackupItemAction) Execute(item runtime.Unstructured, backup *api.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { + delegate, err := r.getDelegate() + if err != nil { + return nil, nil, err + } + + return delegate.Execute(item, backup) +} + +// Version 2: simply discard ctx and call version 1 function. +// ExecuteV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1BackupItemAction) ExecuteV2(ctx context.Context, item runtime.Unstructured, backup *api.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { + delegate, err := r.getDelegate() + if err != nil { + return nil, nil, err + } + return delegate.Execute(item, backup) +} diff --git a/pkg/plugin/clientmgmt/restartable_adapted_v1_object_store.go b/pkg/plugin/clientmgmt/restartable_adapted_v1_object_store.go new file mode 100644 index 000000000..a29766b7d --- /dev/null +++ b/pkg/plugin/clientmgmt/restartable_adapted_v1_object_store.go @@ -0,0 +1,238 @@ +/* +Copyright 2021 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 clientmgmt + +import ( + "context" + "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "io" + "time" + + "github.com/pkg/errors" + objectstorev1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/objectstore/v1" +) + +// restartableAdaptedV1ObjectStore is restartableAdaptedV1ObjectStore version 1 adaptive to version 2 plugin +type restartableAdaptedV1ObjectStore struct { + restartableObjectStore +} + +// newAdaptedV1ObjectStore returns a new restartableAdaptedV1ObjectStore. +func newAdaptedV1ObjectStore(name string, sharedPluginProcess RestartableProcess) *restartableAdaptedV1ObjectStore { + key := kindAndName{kind: framework.PluginKindObjectStore, name: name} + r := &restartableAdaptedV1ObjectStore{ + restartableObjectStore: restartableObjectStore{ + key: key, + sharedPluginProcess: sharedPluginProcess, + }, + } + + // Register our reinitializer so we can reinitialize after a restart with r.config. + sharedPluginProcess.addReinitializer(key, r) + return r +} + +// reinitialize reinitializes a re-dispensed plugin using the initial data passed to Init(). +func (r *restartableAdaptedV1ObjectStore) reinitialize(dispensed interface{}) error { + objectStore, ok := dispensed.(objectstorev1.ObjectStore) + if !ok { + return errors.Errorf("%T is not a ObjectStore!", dispensed) + } + + return r.init(objectStore, r.config) +} + +// getObjectStore returns the object store for this restartableObjectStore. It does *not* restart the +// plugin process. +func (r *restartableAdaptedV1ObjectStore) getObjectStore() (objectstorev1.ObjectStore, error) { + plugin, err := r.sharedPluginProcess.getByKindAndName(r.key) + if err != nil { + return nil, err + } + + objectStore, ok := plugin.(objectstorev1.ObjectStore) + if !ok { + return nil, errors.Errorf("%T is not a ObjectStore!", plugin) + } + + return objectStore, nil +} + +// getDelegate restarts the plugin process (if needed) and returns the object store for this restartableObjectStore. +func (r *restartableAdaptedV1ObjectStore) getDelegate() (objectstorev1.ObjectStore, error) { + if err := r.sharedPluginProcess.resetIfNeeded(); err != nil { + return nil, err + } + + return r.getObjectStore() +} + +// Init initializes the object store instance using config. If this is the first invocation, r stores config for future +// reinitialization needs. Init does NOT restart the shared plugin process. Init may only be called once. +func (r *restartableAdaptedV1ObjectStore) Init(config map[string]string) error { + if r.config != nil { + return errors.Errorf("already initialized") + } + + // Not using getDelegate() to avoid possible infinite recursion + delegate, err := r.getObjectStore() + if err != nil { + return err + } + + r.config = config + + return r.init(delegate, config) +} + +func (r *restartableAdaptedV1ObjectStore) InitV2(ctx context.Context, config map[string]string) error { + return r.Init(config) +} + +// init calls Init on objectStore with config. This is split out from Init() so that both Init() and reinitialize() may +// call it using a specific ObjectStore. +func (r *restartableAdaptedV1ObjectStore) init(objectStore objectstorev1.ObjectStore, config map[string]string) error { + return objectStore.Init(config) +} + +// PutObject restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1ObjectStore) PutObject(bucket string, key string, body io.Reader) error { + delegate, err := r.getDelegate() + if err != nil { + return err + } + return delegate.PutObject(bucket, key, body) +} + +// ObjectExists restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1ObjectStore) ObjectExists(bucket, key string) (bool, error) { + delegate, err := r.getDelegate() + if err != nil { + return false, err + } + return delegate.ObjectExists(bucket, key) +} + +// GetObject restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1ObjectStore) GetObject(bucket string, key string) (io.ReadCloser, error) { + delegate, err := r.getDelegate() + if err != nil { + return nil, err + } + return delegate.GetObject(bucket, key) +} + +// ListCommonPrefixes restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1ObjectStore) ListCommonPrefixes(bucket string, prefix string, delimiter string) ([]string, error) { + delegate, err := r.getDelegate() + if err != nil { + return nil, err + } + return delegate.ListCommonPrefixes(bucket, prefix, delimiter) +} + +// ListObjects restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1ObjectStore) ListObjects(bucket string, prefix string) ([]string, error) { + delegate, err := r.getDelegate() + if err != nil { + return nil, err + } + return delegate.ListObjects(bucket, prefix) +} + +// DeleteObject restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1ObjectStore) DeleteObject(bucket string, key string) error { + delegate, err := r.getDelegate() + if err != nil { + return err + } + return delegate.DeleteObject(bucket, key) +} + +// CreateSignedURL restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1ObjectStore) CreateSignedURL(bucket string, key string, ttl time.Duration) (string, error) { + delegate, err := r.getDelegate() + if err != nil { + return "", err + } + return delegate.CreateSignedURL(bucket, key, ttl) +} + +// Version 2. Simply discard ctx. +// PutObjectV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1ObjectStore) PutObjectV2(ctx context.Context, bucket string, key string, body io.Reader) error { + delegate, err := r.getDelegate() + if err != nil { + return err + } + return delegate.PutObject(bucket, key, body) +} + +// ObjectExistsV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1ObjectStore) ObjectExistsV2(ctx context.Context, bucket, key string) (bool, error) { + delegate, err := r.getDelegate() + if err != nil { + return false, err + } + return delegate.ObjectExists(bucket, key) +} + +// GetObjectV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1ObjectStore) GetObjectV2(ctx context.Context, bucket string, key string) (io.ReadCloser, error) { + delegate, err := r.getDelegate() + if err != nil { + return nil, err + } + return delegate.GetObject(bucket, key) +} + +// ListCommonPrefixesV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1ObjectStore) ListCommonPrefixesV2( + ctx context.Context, bucket string, prefix string, delimiter string) ([]string, error) { + delegate, err := r.getDelegate() + if err != nil { + return nil, err + } + return delegate.ListCommonPrefixes(bucket, prefix, delimiter) +} + +// ListObjectsV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1ObjectStore) ListObjectsV2(ctx context.Context, bucket string, prefix string) ([]string, error) { + delegate, err := r.getDelegate() + if err != nil { + return nil, err + } + return delegate.ListObjects(bucket, prefix) +} + +// DeleteObjectV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1ObjectStore) DeleteObjectV2(ctx context.Context, bucket string, key string) error { + delegate, err := r.getDelegate() + if err != nil { + return err + } + return delegate.DeleteObject(bucket, key) +} + +// CreateSignedURLV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1ObjectStore) CreateSignedURLV2(ctx context.Context, bucket string, key string, ttl time.Duration) (string, error) { + delegate, err := r.getDelegate() + if err != nil { + return "", err + } + return delegate.CreateSignedURL(bucket, key, ttl) +} diff --git a/pkg/plugin/clientmgmt/restartable_adapted_v1_restore_item_action.go b/pkg/plugin/clientmgmt/restartable_adapted_v1_restore_item_action.go new file mode 100644 index 000000000..c333aadb0 --- /dev/null +++ b/pkg/plugin/clientmgmt/restartable_adapted_v1_restore_item_action.go @@ -0,0 +1,97 @@ +/* +Copyright 2021 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 clientmgmt + +import ( + "context" + + "github.com/pkg/errors" + "github.com/vmware-tanzu/velero/pkg/plugin/framework" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" + restoreitemactionv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" +) + +type restartableAdaptedV1RestoreItemAction struct { + key kindAndName + sharedPluginProcess RestartableProcess + config map[string]string +} + +// newRestartableRestoreItemAction returns a new restartableRestoreItemAction. +func newAdaptedV1RestoreItemAction(name string, sharedPluginProcess RestartableProcess) *restartableAdaptedV1RestoreItemAction { + r := &restartableAdaptedV1RestoreItemAction{ + key: kindAndName{kind: framework.PluginKindRestoreItemAction, name: name}, + sharedPluginProcess: sharedPluginProcess, + } + return r +} + +// getRestoreItemAction returns the restore item action for this restartableRestoreItemAction. It does *not* restart the +// plugin process. +func (r *restartableAdaptedV1RestoreItemAction) getRestoreItemAction() (restoreitemactionv1.RestoreItemAction, error) { + plugin, err := r.sharedPluginProcess.getByKindAndName(r.key) + if err != nil { + return nil, err + } + + restoreItemAction, ok := plugin.(restoreitemactionv1.RestoreItemAction) + if !ok { + return nil, errors.Errorf("%T is not a RestoreItemAction!", plugin) + } + + return restoreItemAction, nil +} + +// getDelegate restarts the plugin process (if needed) and returns the restore item action for this restartableRestoreItemAction. +func (r *restartableAdaptedV1RestoreItemAction) getDelegate() (restoreitemactionv1.RestoreItemAction, error) { + if err := r.sharedPluginProcess.resetIfNeeded(); err != nil { + return nil, err + } + + return r.getRestoreItemAction() +} + +// AppliesTo restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1RestoreItemAction) AppliesTo() (velero.ResourceSelector, error) { + delegate, err := r.getDelegate() + if err != nil { + return velero.ResourceSelector{}, err + } + + return delegate.AppliesTo() +} + +// Execute restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1RestoreItemAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { + delegate, err := r.getDelegate() + if err != nil { + return nil, err + } + + return delegate.Execute(input) +} + +// ExecuteV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableAdaptedV1RestoreItemAction) ExecuteV2( + ctx context.Context, input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { + delegate, err := r.getDelegate() + if err != nil { + return nil, err + } + + return delegate.Execute(input) +} diff --git a/pkg/plugin/clientmgmt/restartable_backup_item_action.go b/pkg/plugin/clientmgmt/restartable_backup_item_action.go index c8e96ab80..58c11245a 100644 --- a/pkg/plugin/clientmgmt/restartable_backup_item_action.go +++ b/pkg/plugin/clientmgmt/restartable_backup_item_action.go @@ -1,5 +1,5 @@ /* -Copyright 2018 the Velero contributors. +Copyright 2018, 2021 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. @@ -17,12 +17,15 @@ limitations under the License. package clientmgmt import ( + "context" + "github.com/pkg/errors" "k8s.io/apimachinery/pkg/runtime" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + backupitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2" ) // restartableBackupItemAction is a backup item action for a given implementation (such as "pod"). It is associated with @@ -34,10 +37,10 @@ type restartableBackupItemAction struct { sharedPluginProcess RestartableProcess } -// newRestartableBackupItemAction returns a new restartableBackupItemAction. -func newRestartableBackupItemAction(name string, sharedPluginProcess RestartableProcess) *restartableBackupItemAction { +// newRestartableBackupItemActionV2 returns a new restartableBackupItemAction. +func newRestartableBackupItemActionV2(name string, sharedPluginProcess RestartableProcess) *restartableBackupItemAction { r := &restartableBackupItemAction{ - key: kindAndName{kind: framework.PluginKindBackupItemAction, name: name}, + key: kindAndName{kind: framework.PluginKindBackupItemActionV2, name: name}, sharedPluginProcess: sharedPluginProcess, } return r @@ -45,13 +48,13 @@ func newRestartableBackupItemAction(name string, sharedPluginProcess Restartable // getBackupItemAction returns the backup item action for this restartableBackupItemAction. It does *not* restart the // plugin process. -func (r *restartableBackupItemAction) getBackupItemAction() (velero.BackupItemAction, error) { +func (r *restartableBackupItemAction) getBackupItemAction() (backupitemactionv2.BackupItemAction, error) { plugin, err := r.sharedPluginProcess.getByKindAndName(r.key) if err != nil { return nil, err } - backupItemAction, ok := plugin.(velero.BackupItemAction) + backupItemAction, ok := plugin.(backupitemactionv2.BackupItemAction) if !ok { return nil, errors.Errorf("%T is not a BackupItemAction!", plugin) } @@ -60,7 +63,7 @@ func (r *restartableBackupItemAction) getBackupItemAction() (velero.BackupItemAc } // getDelegate restarts the plugin process (if needed) and returns the backup item action for this restartableBackupItemAction. -func (r *restartableBackupItemAction) getDelegate() (velero.BackupItemAction, error) { +func (r *restartableBackupItemAction) getDelegate() (backupitemactionv2.BackupItemAction, error) { if err := r.sharedPluginProcess.resetIfNeeded(); err != nil { return nil, err } @@ -87,3 +90,13 @@ func (r *restartableBackupItemAction) Execute(item runtime.Unstructured, backup return delegate.Execute(item, backup) } + +// ExecuteV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableBackupItemAction) ExecuteV2(ctx context.Context, item runtime.Unstructured, backup *api.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { + delegate, err := r.getDelegate() + if err != nil { + return nil, nil, err + } + + return delegate.ExecuteV2(ctx, item, backup) +} diff --git a/pkg/plugin/clientmgmt/restartable_delete_item_action.go b/pkg/plugin/clientmgmt/restartable_delete_item_action.go index 266500c7a..68acff69d 100644 --- a/pkg/plugin/clientmgmt/restartable_delete_item_action.go +++ b/pkg/plugin/clientmgmt/restartable_delete_item_action.go @@ -17,10 +17,14 @@ limitations under the License. package clientmgmt import ( + "context" + "github.com/pkg/errors" - "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + + "github.com/vmware-tanzu/velero/pkg/plugin/framework" + deleteitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/deleteitemaction/v2" ) // restartableDeleteItemAction is a delete item action for a given implementation (such as "pod"). It is associated with @@ -34,7 +38,7 @@ type restartableDeleteItemAction struct { } // newRestartableDeleteItemAction returns a new restartableDeleteItemAction. -func newRestartableDeleteItemAction(name string, sharedPluginProcess RestartableProcess) *restartableDeleteItemAction { +func newRestartableDeleteItemActionV2(name string, sharedPluginProcess RestartableProcess) *restartableDeleteItemAction { r := &restartableDeleteItemAction{ key: kindAndName{kind: framework.PluginKindDeleteItemAction, name: name}, sharedPluginProcess: sharedPluginProcess, @@ -44,13 +48,13 @@ func newRestartableDeleteItemAction(name string, sharedPluginProcess Restartable // getDeleteItemAction returns the delete item action for this restartableDeleteItemAction. It does *not* restart the // plugin process. -func (r *restartableDeleteItemAction) getDeleteItemAction() (velero.DeleteItemAction, error) { +func (r *restartableDeleteItemAction) getDeleteItemAction() (deleteitemactionv2.DeleteItemAction, error) { plugin, err := r.sharedPluginProcess.getByKindAndName(r.key) if err != nil { return nil, err } - deleteItemAction, ok := plugin.(velero.DeleteItemAction) + deleteItemAction, ok := plugin.(deleteitemactionv2.DeleteItemAction) if !ok { return nil, errors.Errorf("%T is not a DeleteItemAction!", plugin) } @@ -59,7 +63,7 @@ func (r *restartableDeleteItemAction) getDeleteItemAction() (velero.DeleteItemAc } // getDelegate restarts the plugin process (if needed) and returns the delete item action for this restartableDeleteItemAction. -func (r *restartableDeleteItemAction) getDelegate() (velero.DeleteItemAction, error) { +func (r *restartableDeleteItemAction) getDelegate() (deleteitemactionv2.DeleteItemAction, error) { if err := r.sharedPluginProcess.resetIfNeeded(); err != nil { return nil, err } @@ -86,3 +90,13 @@ func (r *restartableDeleteItemAction) Execute(input *velero.DeleteItemActionExec return delegate.Execute(input) } + +// ExecuteV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableDeleteItemAction) ExecuteV2(ctx context.Context, input *velero.DeleteItemActionExecuteInput) error { + delegate, err := r.getDelegate() + if err != nil { + return err + } + + return delegate.ExecuteV2(ctx, input) +} diff --git a/pkg/plugin/clientmgmt/restartable_object_store.go b/pkg/plugin/clientmgmt/restartable_object_store.go index 95fbc75c5..9550c8911 100644 --- a/pkg/plugin/clientmgmt/restartable_object_store.go +++ b/pkg/plugin/clientmgmt/restartable_object_store.go @@ -1,5 +1,5 @@ /* -Copyright 2018 the Velero contributors. +Copyright 2021 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. @@ -17,13 +17,14 @@ limitations under the License. package clientmgmt import ( + "context" "io" "time" "github.com/pkg/errors" "github.com/vmware-tanzu/velero/pkg/plugin/framework" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + objectstorev2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/objectstore/v2" ) // restartableObjectStore is an object store for a given implementation (such as "aws"). It is associated with @@ -38,9 +39,9 @@ type restartableObjectStore struct { config map[string]string } -// newRestartableObjectStore returns a new restartableObjectStore. -func newRestartableObjectStore(name string, sharedPluginProcess RestartableProcess) *restartableObjectStore { - key := kindAndName{kind: framework.PluginKindObjectStore, name: name} +// newRestartableObjectStoreV2 returns a new restartableObjectStore for version 2. +func newRestartableObjectStoreV2(name string, sharedPluginProcess RestartableProcess) *restartableObjectStore { + key := kindAndName{kind: framework.PluginKindObjectStoreV2, name: name} r := &restartableObjectStore{ key: key, sharedPluginProcess: sharedPluginProcess, @@ -54,7 +55,7 @@ func newRestartableObjectStore(name string, sharedPluginProcess RestartableProce // reinitialize reinitializes a re-dispensed plugin using the initial data passed to Init(). func (r *restartableObjectStore) reinitialize(dispensed interface{}) error { - objectStore, ok := dispensed.(velero.ObjectStore) + objectStore, ok := dispensed.(objectstorev2.ObjectStore) if !ok { return errors.Errorf("%T is not a ObjectStore!", dispensed) } @@ -64,13 +65,13 @@ func (r *restartableObjectStore) reinitialize(dispensed interface{}) error { // getObjectStore returns the object store for this restartableObjectStore. It does *not* restart the // plugin process. -func (r *restartableObjectStore) getObjectStore() (velero.ObjectStore, error) { +func (r *restartableObjectStore) getObjectStore() (objectstorev2.ObjectStore, error) { plugin, err := r.sharedPluginProcess.getByKindAndName(r.key) if err != nil { return nil, err } - objectStore, ok := plugin.(velero.ObjectStore) + objectStore, ok := plugin.(objectstorev2.ObjectStore) if !ok { return nil, errors.Errorf("%T is not a ObjectStore!", plugin) } @@ -79,7 +80,7 @@ func (r *restartableObjectStore) getObjectStore() (velero.ObjectStore, error) { } // getDelegate restarts the plugin process (if needed) and returns the object store for this restartableObjectStore. -func (r *restartableObjectStore) getDelegate() (velero.ObjectStore, error) { +func (r *restartableObjectStore) getDelegate() (objectstorev2.ObjectStore, error) { if err := r.sharedPluginProcess.resetIfNeeded(); err != nil { return nil, err } @@ -105,9 +106,15 @@ func (r *restartableObjectStore) Init(config map[string]string) error { return r.init(delegate, config) } +// InitV2 initializes the object store instance using config. If this is the first invocation, r stores config for future +// reinitialization needs. Init does NOT restart the shared plugin process. Init may only be called once. +func (r *restartableObjectStore) InitV2(ctx context.Context, config map[string]string) error { + return r.Init(config) +} + // init calls Init on objectStore with config. This is split out from Init() so that both Init() and reinitialize() may // call it using a specific ObjectStore. -func (r *restartableObjectStore) init(objectStore velero.ObjectStore, config map[string]string) error { +func (r *restartableObjectStore) init(objectStore objectstorev2.ObjectStore, config map[string]string) error { return objectStore.Init(config) } @@ -173,3 +180,68 @@ func (r *restartableObjectStore) CreateSignedURL(bucket string, key string, ttl } return delegate.CreateSignedURL(bucket, key, ttl) } + +// Version 2 +// PutObjectV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableObjectStore) PutObjectV2(ctx context.Context, bucket string, key string, body io.Reader) error { + delegate, err := r.getDelegate() + if err != nil { + return err + } + return delegate.PutObjectV2(ctx, bucket, key, body) +} + +// ObjectExistsV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableObjectStore) ObjectExistsV2(ctx context.Context, bucket, key string) (bool, error) { + delegate, err := r.getDelegate() + if err != nil { + return false, err + } + return delegate.ObjectExistsV2(ctx, bucket, key) +} + +// GetObjectV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableObjectStore) GetObjectV2(ctx context.Context, bucket string, key string) (io.ReadCloser, error) { + delegate, err := r.getDelegate() + if err != nil { + return nil, err + } + return delegate.GetObjectV2(ctx, bucket, key) +} + +// ListCommonPrefixesV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableObjectStore) ListCommonPrefixesV2( + ctx context.Context, bucket string, prefix string, delimiter string) ([]string, error) { + delegate, err := r.getDelegate() + if err != nil { + return nil, err + } + return delegate.ListCommonPrefixesV2(ctx, bucket, prefix, delimiter) +} + +// ListObjectsV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableObjectStore) ListObjectsV2(ctx context.Context, bucket string, prefix string) ([]string, error) { + delegate, err := r.getDelegate() + if err != nil { + return nil, err + } + return delegate.ListObjectsV2(ctx, bucket, prefix) +} + +// DeleteObjectV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableObjectStore) DeleteObjectV2(ctx context.Context, bucket string, key string) error { + delegate, err := r.getDelegate() + if err != nil { + return err + } + return delegate.DeleteObjectV2(ctx, bucket, key) +} + +// CreateSignedURLV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableObjectStore) CreateSignedURLV2(ctx context.Context, bucket string, key string, ttl time.Duration) (string, error) { + delegate, err := r.getDelegate() + if err != nil { + return "", err + } + return delegate.CreateSignedURLV2(ctx, bucket, key, ttl) +} diff --git a/pkg/plugin/clientmgmt/restartable_restore_item_action.go b/pkg/plugin/clientmgmt/restartable_restore_item_action.go index 0f55ebfde..25943eb4d 100644 --- a/pkg/plugin/clientmgmt/restartable_restore_item_action.go +++ b/pkg/plugin/clientmgmt/restartable_restore_item_action.go @@ -1,5 +1,5 @@ /* -Copyright 2018 the Velero contributors. +Copyright 2021 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. @@ -17,10 +17,12 @@ limitations under the License. package clientmgmt import ( - "github.com/pkg/errors" + "context" + "github.com/pkg/errors" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + restoreitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2" ) // restartableRestoreItemAction is a restore item action for a given implementation (such as "pod"). It is associated with @@ -33,10 +35,10 @@ type restartableRestoreItemAction struct { config map[string]string } -// newRestartableRestoreItemAction returns a new restartableRestoreItemAction. -func newRestartableRestoreItemAction(name string, sharedPluginProcess RestartableProcess) *restartableRestoreItemAction { +// newRestartableRestoreItemActionV2 returns a new restartableRestoreItemAction. +func newRestartableRestoreItemActionV2(name string, sharedPluginProcess RestartableProcess) *restartableRestoreItemAction { r := &restartableRestoreItemAction{ - key: kindAndName{kind: framework.PluginKindRestoreItemAction, name: name}, + key: kindAndName{kind: framework.PluginKindRestoreItemActionV2, name: name}, sharedPluginProcess: sharedPluginProcess, } return r @@ -44,13 +46,13 @@ func newRestartableRestoreItemAction(name string, sharedPluginProcess Restartabl // getRestoreItemAction returns the restore item action for this restartableRestoreItemAction. It does *not* restart the // plugin process. -func (r *restartableRestoreItemAction) getRestoreItemAction() (velero.RestoreItemAction, error) { +func (r *restartableRestoreItemAction) getRestoreItemAction() (restoreitemactionv2.RestoreItemAction, error) { plugin, err := r.sharedPluginProcess.getByKindAndName(r.key) if err != nil { return nil, err } - restoreItemAction, ok := plugin.(velero.RestoreItemAction) + restoreItemAction, ok := plugin.(restoreitemactionv2.RestoreItemAction) if !ok { return nil, errors.Errorf("%T is not a RestoreItemAction!", plugin) } @@ -59,7 +61,7 @@ func (r *restartableRestoreItemAction) getRestoreItemAction() (velero.RestoreIte } // getDelegate restarts the plugin process (if needed) and returns the restore item action for this restartableRestoreItemAction. -func (r *restartableRestoreItemAction) getDelegate() (velero.RestoreItemAction, error) { +func (r *restartableRestoreItemAction) getDelegate() (restoreitemactionv2.RestoreItemAction, error) { if err := r.sharedPluginProcess.resetIfNeeded(); err != nil { return nil, err } @@ -86,3 +88,14 @@ func (r *restartableRestoreItemAction) Execute(input *velero.RestoreItemActionEx return delegate.Execute(input) } + +// ExecuteV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableRestoreItemAction) ExecuteV2( + ctx context.Context, input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { + delegate, err := r.getDelegate() + if err != nil { + return nil, err + } + + return delegate.ExecuteV2(ctx, input) +} diff --git a/pkg/plugin/clientmgmt/restartable_volume_snapshotter.go b/pkg/plugin/clientmgmt/restartable_volume_snapshotter.go index 5e46bf229..fd8c383c9 100644 --- a/pkg/plugin/clientmgmt/restartable_volume_snapshotter.go +++ b/pkg/plugin/clientmgmt/restartable_volume_snapshotter.go @@ -1,5 +1,5 @@ /* -Copyright 2018 the Velero contributors. +Copyright 2021 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. @@ -17,11 +17,13 @@ limitations under the License. package clientmgmt import ( + "context" + "github.com/pkg/errors" "k8s.io/apimachinery/pkg/runtime" "github.com/vmware-tanzu/velero/pkg/plugin/framework" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + volumesnapshotterv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v2" ) // restartableVolumeSnapshotter is a volume snapshotter for a given implementation (such as "aws"). It is associated with @@ -34,9 +36,9 @@ type restartableVolumeSnapshotter struct { config map[string]string } -// newRestartableVolumeSnapshotter returns a new restartableVolumeSnapshotter. -func newRestartableVolumeSnapshotter(name string, sharedPluginProcess RestartableProcess) *restartableVolumeSnapshotter { - key := kindAndName{kind: framework.PluginKindVolumeSnapshotter, name: name} +// newRestartableVolumeSnapshotterV2 returns a new restartableVolumeSnapshotter. +func newRestartableVolumeSnapshotterV2(name string, sharedPluginProcess RestartableProcess) *restartableVolumeSnapshotter { + key := kindAndName{kind: framework.PluginKindVolumeSnapshotterV2, name: name} r := &restartableVolumeSnapshotter{ key: key, sharedPluginProcess: sharedPluginProcess, @@ -50,7 +52,7 @@ func newRestartableVolumeSnapshotter(name string, sharedPluginProcess Restartabl // reinitialize reinitializes a re-dispensed plugin using the initial data passed to Init(). func (r *restartableVolumeSnapshotter) reinitialize(dispensed interface{}) error { - volumeSnapshotter, ok := dispensed.(velero.VolumeSnapshotter) + volumeSnapshotter, ok := dispensed.(volumesnapshotterv2.VolumeSnapshotter) if !ok { return errors.Errorf("%T is not a VolumeSnapshotter!", dispensed) } @@ -59,13 +61,13 @@ func (r *restartableVolumeSnapshotter) reinitialize(dispensed interface{}) error // getVolumeSnapshotter returns the volume snapshotter for this restartableVolumeSnapshotter. It does *not* restart the // plugin process. -func (r *restartableVolumeSnapshotter) getVolumeSnapshotter() (velero.VolumeSnapshotter, error) { +func (r *restartableVolumeSnapshotter) getVolumeSnapshotter() (volumesnapshotterv2.VolumeSnapshotter, error) { plugin, err := r.sharedPluginProcess.getByKindAndName(r.key) if err != nil { return nil, err } - volumeSnapshotter, ok := plugin.(velero.VolumeSnapshotter) + volumeSnapshotter, ok := plugin.(volumesnapshotterv2.VolumeSnapshotter) if !ok { return nil, errors.Errorf("%T is not a VolumeSnapshotter!", plugin) } @@ -74,7 +76,7 @@ func (r *restartableVolumeSnapshotter) getVolumeSnapshotter() (velero.VolumeSnap } // getDelegate restarts the plugin process (if needed) and returns the volume snapshotter for this restartableVolumeSnapshotter. -func (r *restartableVolumeSnapshotter) getDelegate() (velero.VolumeSnapshotter, error) { +func (r *restartableVolumeSnapshotter) getDelegate() (volumesnapshotterv2.VolumeSnapshotter, error) { if err := r.sharedPluginProcess.resetIfNeeded(); err != nil { return nil, err } @@ -102,7 +104,7 @@ func (r *restartableVolumeSnapshotter) Init(config map[string]string) error { // init calls Init on volumeSnapshotter with config. This is split out from Init() so that both Init() and reinitialize() may // call it using a specific VolumeSnapshotter. -func (r *restartableVolumeSnapshotter) init(volumeSnapshotter velero.VolumeSnapshotter, config map[string]string) error { +func (r *restartableVolumeSnapshotter) init(volumeSnapshotter volumesnapshotterv2.VolumeSnapshotter, config map[string]string) error { return volumeSnapshotter.Init(config) } @@ -115,6 +117,7 @@ func (r *restartableVolumeSnapshotter) CreateVolumeFromSnapshot(snapshotID strin return delegate.CreateVolumeFromSnapshot(snapshotID, volumeType, volumeAZ, iops) } + // GetVolumeID restarts the plugin's process if needed, then delegates the call. func (r *restartableVolumeSnapshotter) GetVolumeID(pv runtime.Unstructured) (string, error) { delegate, err := r.getDelegate() @@ -159,3 +162,66 @@ func (r *restartableVolumeSnapshotter) DeleteSnapshot(snapshotID string) error { } return delegate.DeleteSnapshot(snapshotID) } + +// Version 2 +func (r *restartableVolumeSnapshotter) InitV2(ctx context.Context, config map[string]string) error { + return r.Init(config) +} +// CreateVolumeFromSnapshotV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableVolumeSnapshotter) CreateVolumeFromSnapshotV2( + ctx context.Context, snapshotID string, volumeType string, volumeAZ string, iops *int64) (volumeID string, err error) { + delegate, err := r.getDelegate() + if err != nil { + return "", err + } + return delegate.CreateVolumeFromSnapshotV2(ctx, snapshotID, volumeType, volumeAZ, iops) +} + +// GetVolumeIDV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableVolumeSnapshotter) GetVolumeIDV2( + ctx context.Context, pv runtime.Unstructured) (string, error) { + delegate, err := r.getDelegate() + if err != nil { + return "", err + } + return delegate.GetVolumeIDV2(ctx, pv) +} + +// SetVolumeIDV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableVolumeSnapshotter) SetVolumeIDV2( + ctx context.Context, pv runtime.Unstructured, volumeID string) (runtime.Unstructured, error) { + delegate, err := r.getDelegate() + if err != nil { + return nil, err + } + return delegate.SetVolumeIDV2(ctx, pv, volumeID) +} + +// GetVolumeInfoV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableVolumeSnapshotter) GetVolumeInfoV2( + ctx context.Context, volumeID string, volumeAZ string) (string, *int64, error) { + delegate, err := r.getDelegate() + if err != nil { + return "", nil, err + } + return delegate.GetVolumeInfoV2(ctx, volumeID, volumeAZ) +} + +// CreateSnapshotV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableVolumeSnapshotter) CreateSnapshotV2( + ctx context.Context, volumeID string, volumeAZ string, tags map[string]string) (snapshotID string, err error) { + delegate, err := r.getDelegate() + if err != nil { + return "", err + } + return delegate.CreateSnapshotV2(ctx, volumeID, volumeAZ, tags) +} + +// DeleteSnapshotV2 restarts the plugin's process if needed, then delegates the call. +func (r *restartableVolumeSnapshotter) DeleteSnapshotV2(ctx context.Context, snapshotID string) error { + delegate, err := r.getDelegate() + if err != nil { + return err + } + return delegate.DeleteSnapshotV2(ctx, snapshotID) +} diff --git a/pkg/plugin/framework/backup_item_action.go b/pkg/plugin/framework/backup_item_action.go index 1dc7bf3e1..293016a09 100644 --- a/pkg/plugin/framework/backup_item_action.go +++ b/pkg/plugin/framework/backup_item_action.go @@ -1,5 +1,5 @@ /* -Copyright 2019 the Velero contributors. +Copyright 2021 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. diff --git a/pkg/plugin/framework/backup_item_action_client.go b/pkg/plugin/framework/backup_item_action_client.go index b48de3928..4537afc18 100644 --- a/pkg/plugin/framework/backup_item_action_client.go +++ b/pkg/plugin/framework/backup_item_action_client.go @@ -119,3 +119,51 @@ func (c *BackupItemActionGRPCClient) Execute(item runtime.Unstructured, backup * return &updatedItem, additionalItems, nil } + +func (c *BackupItemActionGRPCClient) ExecuteV2( + ctx context.Context, item runtime.Unstructured, backup *api.Backup) ( + runtime.Unstructured, []velero.ResourceIdentifier, error) { + + itemJSON, err := json.Marshal(item.UnstructuredContent()) + if err != nil { + return nil, nil, errors.WithStack(err) + } + + backupJSON, err := json.Marshal(backup) + if err != nil { + return nil, nil, errors.WithStack(err) + } + + req := &proto.ExecuteRequest{ + Plugin: c.plugin, + Item: itemJSON, + Backup: backupJSON, + } + + res, err := c.grpcClient.Execute(ctx, req) + if err != nil { + return nil, nil, fromGRPCError(err) + } + + var updatedItem unstructured.Unstructured + if err := json.Unmarshal(res.Item, &updatedItem); err != nil { + return nil, nil, errors.WithStack(err) + } + + var additionalItems []velero.ResourceIdentifier + + for _, itm := range res.AdditionalItems { + newItem := velero.ResourceIdentifier{ + GroupResource: schema.GroupResource{ + Group: itm.Group, + Resource: itm.Resource, + }, + Namespace: itm.Namespace, + Name: itm.Name, + } + + additionalItems = append(additionalItems, newItem) + } + + return &updatedItem, additionalItems, nil +} diff --git a/pkg/plugin/framework/backup_item_action_server.go b/pkg/plugin/framework/backup_item_action_server.go index fb4b8a6af..1f44a89ae 100644 --- a/pkg/plugin/framework/backup_item_action_server.go +++ b/pkg/plugin/framework/backup_item_action_server.go @@ -26,6 +26,7 @@ import ( api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + backupitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2" ) // BackupItemActionGRPCServer implements the proto-generated BackupItemAction interface, and accepts @@ -34,13 +35,13 @@ type BackupItemActionGRPCServer struct { mux *serverMux } -func (s *BackupItemActionGRPCServer) getImpl(name string) (velero.BackupItemAction, error) { +func (s *BackupItemActionGRPCServer) getImpl(name string) (backupitemactionv2.BackupItemAction, error) { impl, err := s.mux.getHandler(name) if err != nil { return nil, err } - itemAction, ok := impl.(velero.BackupItemAction) + itemAction, ok := impl.(backupitemactionv2.BackupItemAction) if !ok { return nil, errors.Errorf("%T is not a backup item action", impl) } @@ -98,7 +99,7 @@ func (s *BackupItemActionGRPCServer) Execute(ctx context.Context, req *proto.Exe return nil, newGRPCError(errors.WithStack(err)) } - updatedItem, additionalItems, err := impl.Execute(&item, &backup) + updatedItem, additionalItems, err := impl.ExecuteV2(ctx, &item, &backup) if err != nil { return nil, newGRPCError(err) } diff --git a/pkg/plugin/framework/delete_item_action_client.go b/pkg/plugin/framework/delete_item_action_client.go index e9adae6d0..3ee7e3426 100644 --- a/pkg/plugin/framework/delete_item_action_client.go +++ b/pkg/plugin/framework/delete_item_action_client.go @@ -25,9 +25,10 @@ import ( proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + deleteitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/deleteitemaction/v2" ) -var _ velero.DeleteItemAction = &DeleteItemActionGRPCClient{} +var _ deleteitemactionv2.DeleteItemAction = &DeleteItemActionGRPCClient{} // NewDeleteItemActionPlugin constructs a DeleteItemActionPlugin. func NewDeleteItemActionPlugin(options ...PluginOption) *DeleteItemActionPlugin { @@ -93,3 +94,28 @@ func (c *DeleteItemActionGRPCClient) Execute(input *velero.DeleteItemActionExecu return nil } + +func (c *DeleteItemActionGRPCClient) ExecuteV2(ctx context.Context, input *velero.DeleteItemActionExecuteInput) error { + itemJSON, err := json.Marshal(input.Item.UnstructuredContent()) + if err != nil { + return errors.WithStack(err) + } + + backupJSON, err := json.Marshal(input.Backup) + if err != nil { + return errors.WithStack(err) + } + + req := &proto.DeleteItemActionExecuteRequest{ + Plugin: c.plugin, + Item: itemJSON, + Backup: backupJSON, + } + + // First return item is just an empty struct no matter what. + if _, err = c.grpcClient.Execute(ctx, req); err != nil { + return fromGRPCError(err) + } + + return nil +} diff --git a/pkg/plugin/framework/delete_item_action_server.go b/pkg/plugin/framework/delete_item_action_server.go index 3c6be3b54..460175d7c 100644 --- a/pkg/plugin/framework/delete_item_action_server.go +++ b/pkg/plugin/framework/delete_item_action_server.go @@ -26,6 +26,7 @@ import ( api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + deleteitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/deleteitemaction/v2" ) // DeleteItemActionGRPCServer implements the proto-generated DeleteItemActionServer interface, and accepts @@ -34,13 +35,13 @@ type DeleteItemActionGRPCServer struct { mux *serverMux } -func (s *DeleteItemActionGRPCServer) getImpl(name string) (velero.DeleteItemAction, error) { +func (s *DeleteItemActionGRPCServer) getImpl(name string) (deleteitemactionv2.DeleteItemAction, error) { impl, err := s.mux.getHandler(name) if err != nil { return nil, err } - itemAction, ok := impl.(velero.DeleteItemAction) + itemAction, ok := impl.(deleteitemactionv2.DeleteItemAction) if !ok { return nil, errors.Errorf("%T is not a delete item action", impl) } @@ -76,7 +77,8 @@ func (s *DeleteItemActionGRPCServer) AppliesTo(ctx context.Context, req *proto.D }, nil } -func (s *DeleteItemActionGRPCServer) Execute(ctx context.Context, req *proto.DeleteItemActionExecuteRequest) (_ *proto.Empty, err error) { +func (s *DeleteItemActionGRPCServer) Execute( + ctx context.Context, req *proto.DeleteItemActionExecuteRequest) (_ *proto.Empty, err error) { defer func() { if recoveredErr := handlePanic(recover()); recoveredErr != nil { err = recoveredErr @@ -101,7 +103,7 @@ func (s *DeleteItemActionGRPCServer) Execute(ctx context.Context, req *proto.Del return nil, newGRPCError(errors.WithStack(err)) } - if err := impl.Execute(&velero.DeleteItemActionExecuteInput{ + if err := impl.ExecuteV2(ctx, &velero.DeleteItemActionExecuteInput{ Item: &item, Backup: &backup, }); err != nil { diff --git a/pkg/plugin/framework/object_store_client.go b/pkg/plugin/framework/object_store_client.go index 844274907..3557fdafd 100644 --- a/pkg/plugin/framework/object_store_client.go +++ b/pkg/plugin/framework/object_store_client.go @@ -218,3 +218,161 @@ func (c *ObjectStoreGRPCClient) CreateSignedURL(bucket, key string, ttl time.Dur return res.Url, nil } + +// Version 2 +// PutObjectV2 creates a new object using the data in body within the specified +// object storage bucket with the given key. +func (c *ObjectStoreGRPCClient) PutObjectV2(ctx context.Context, bucket, key string, body io.Reader) error { + stream, err := c.grpcClient.PutObject(ctx) + if err != nil { + return fromGRPCError(err) + } + + // read from the provider io.Reader into chunks, and send each one over + // the gRPC stream + chunk := make([]byte, byteChunkSize) + for { + n, err := body.Read(chunk) + if err == io.EOF { + if _, resErr := stream.CloseAndRecv(); resErr != nil { + return fromGRPCError(resErr) + } + return nil + } + if err != nil { + stream.CloseSend() + return errors.WithStack(err) + } + + if err := stream.Send(&proto.PutObjectRequest{Plugin: c.plugin, Bucket: bucket, Key: key, Body: chunk[0:n]}); err != nil { + return fromGRPCError(err) + } + } +} + +// ObjectExistsV2 checks if there is an object with the given key in the object storage bucket. +func (c *ObjectStoreGRPCClient) ObjectExistsV2(ctx context.Context, bucket, key string) (bool, error) { + req := &proto.ObjectExistsRequest{ + Plugin: c.plugin, + Bucket: bucket, + Key: key, + } + + res, err := c.grpcClient.ObjectExists(ctx, req) + if err != nil { + return false, err + } + + return res.Exists, nil +} + +// GetObjectV2 retrieves the object with the given key from the specified +// bucket in object storage. +func (c *ObjectStoreGRPCClient) GetObjectV2(ctx context.Context, bucket, key string) (io.ReadCloser, error) { + req := &proto.GetObjectRequest{ + Plugin: c.plugin, + Bucket: bucket, + Key: key, + } + + stream, err := c.grpcClient.GetObject(ctx, req) + if err != nil { + return nil, fromGRPCError(err) + } + + receive := func() ([]byte, error) { + data, err := stream.Recv() + if err == io.EOF { + // we need to return io.EOF errors unwrapped so that + // calling code sees them as io.EOF and knows to stop + // reading. + return nil, err + } + if err != nil { + return nil, fromGRPCError(err) + } + + return data.Data, nil + } + + close := func() error { + if err := stream.CloseSend(); err != nil { + return fromGRPCError(err) + } + return nil + } + + return &StreamReadCloser{receive: receive, close: close}, nil +} + +// ListCommonPrefixesV2 gets a list of all object key prefixes that come +// after the provided prefix and before the provided delimiter (this is +// often used to simulate a directory hierarchy in object storage). +func (c *ObjectStoreGRPCClient) ListCommonPrefixesV2( + ctx context.Context, bucket, prefix, delimiter string) ([]string, error) { + req := &proto.ListCommonPrefixesRequest{ + Plugin: c.plugin, + Bucket: bucket, + Prefix: prefix, + Delimiter: delimiter, + } + + res, err := c.grpcClient.ListCommonPrefixes(ctx, req) + if err != nil { + return nil, fromGRPCError(err) + } + + return res.Prefixes, nil +} + +// ListObjectsV2 gets a list of all objects in bucket that have the same prefix. +func (c *ObjectStoreGRPCClient) ListObjectsV2( + ctx context.Context, bucket, prefix string) ([]string, error) { + req := &proto.ListObjectsRequest{ + Plugin: c.plugin, + Bucket: bucket, + Prefix: prefix, + } + + res, err := c.grpcClient.ListObjects(ctx, req) + if err != nil { + return nil, fromGRPCError(err) + } + + return res.Keys, nil +} + +// DeleteObjectV2 removes object with the specified key from the given +// bucket. +func (c *ObjectStoreGRPCClient) DeleteObjectV2( + ctx context.Context, bucket, key string) error { + req := &proto.DeleteObjectRequest{ + Plugin: c.plugin, + Bucket: bucket, + Key: key, + } + + if _, err := c.grpcClient.DeleteObject(ctx, req); err != nil { + return fromGRPCError(err) + } + + return nil +} + +// CreateSignedURLV2 creates a pre-signed URL for the given bucket and key that expires after ttl. +func (c *ObjectStoreGRPCClient) CreateSignedURLV2( + ctx context.Context, bucket, key string, ttl time.Duration) (string, error) { + req := &proto.CreateSignedURLRequest{ + Plugin: c.plugin, + Bucket: bucket, + Key: key, + Ttl: int64(ttl), + } + + res, err := c.grpcClient.CreateSignedURL(ctx, req) + if err != nil { + return "", fromGRPCError(err) + } + + return res.Url, nil +} diff --git a/pkg/plugin/framework/object_store_server.go b/pkg/plugin/framework/object_store_server.go index b2f2359ce..832fde879 100644 --- a/pkg/plugin/framework/object_store_server.go +++ b/pkg/plugin/framework/object_store_server.go @@ -24,7 +24,7 @@ import ( "golang.org/x/net/context" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + objectstorev2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/objectstore/v2" ) // ObjectStoreGRPCServer implements the proto-generated ObjectStoreServer interface, and accepts @@ -33,13 +33,13 @@ type ObjectStoreGRPCServer struct { mux *serverMux } -func (s *ObjectStoreGRPCServer) getImpl(name string) (velero.ObjectStore, error) { +func (s *ObjectStoreGRPCServer) getImpl(name string) (objectstorev2.ObjectStore, error) { impl, err := s.mux.getHandler(name) if err != nil { return nil, err } - itemAction, ok := impl.(velero.ObjectStore) + itemAction, ok := impl.(objectstorev2.ObjectStore) if !ok { return nil, errors.Errorf("%T is not an object store", impl) } @@ -62,7 +62,7 @@ func (s *ObjectStoreGRPCServer) Init(ctx context.Context, req *proto.ObjectStore return nil, newGRPCError(err) } - if err := impl.Init(req.Config); err != nil { + if err := impl.InitV2(ctx, req.Config); err != nil { return nil, newGRPCError(err) } @@ -141,7 +141,7 @@ func (s *ObjectStoreGRPCServer) ObjectExists(ctx context.Context, req *proto.Obj return nil, newGRPCError(err) } - exists, err := impl.ObjectExists(req.Bucket, req.Key) + exists, err := impl.ObjectExistsV2(ctx, req.Bucket, req.Key) if err != nil { return nil, newGRPCError(err) } @@ -200,7 +200,7 @@ func (s *ObjectStoreGRPCServer) ListCommonPrefixes(ctx context.Context, req *pro return nil, newGRPCError(err) } - prefixes, err := impl.ListCommonPrefixes(req.Bucket, req.Prefix, req.Delimiter) + prefixes, err := impl.ListCommonPrefixesV2(ctx, req.Bucket, req.Prefix, req.Delimiter) if err != nil { return nil, newGRPCError(err) } @@ -221,7 +221,7 @@ func (s *ObjectStoreGRPCServer) ListObjects(ctx context.Context, req *proto.List return nil, newGRPCError(err) } - keys, err := impl.ListObjects(req.Bucket, req.Prefix) + keys, err := impl.ListObjectsV2(ctx, req.Bucket, req.Prefix) if err != nil { return nil, newGRPCError(err) } @@ -243,7 +243,7 @@ func (s *ObjectStoreGRPCServer) DeleteObject(ctx context.Context, req *proto.Del return nil, newGRPCError(err) } - if err := impl.DeleteObject(req.Bucket, req.Key); err != nil { + if err := impl.DeleteObjectV2(ctx, req.Bucket, req.Key); err != nil { return nil, newGRPCError(err) } @@ -263,7 +263,7 @@ func (s *ObjectStoreGRPCServer) CreateSignedURL(ctx context.Context, req *proto. return nil, newGRPCError(err) } - url, err := impl.CreateSignedURL(req.Bucket, req.Key, time.Duration(req.Ttl)) + url, err := impl.CreateSignedURLV2(ctx, req.Bucket, req.Key, time.Duration(req.Ttl)) if err != nil { return nil, newGRPCError(err) } diff --git a/pkg/plugin/framework/plugin_kinds.go b/pkg/plugin/framework/plugin_kinds.go index 640ae30b8..335dc2e71 100644 --- a/pkg/plugin/framework/plugin_kinds.go +++ b/pkg/plugin/framework/plugin_kinds.go @@ -45,6 +45,27 @@ const ( PluginKindPluginLister PluginKind = "PluginLister" ) +const ( + // PluginKindObjectStoreV2 represents an object store plugin version 2. + PluginKindObjectStoreV2 PluginKind = "ObjectStoreV2" + + // PluginKindVolumeSnapshotterV2 represents a volume snapshotter plugin version 2. + PluginKindVolumeSnapshotterV2 PluginKind = "VolumeSnapshotterV2" + + // PluginKindBackupItemActionV2 represents a backup item action plugin version 2. + PluginKindBackupItemActionV2 PluginKind = "BackupItemActionV2" + + // PluginKindRestoreItemActionV2 represents a restore item action plugin version 2. + PluginKindRestoreItemActionV2 PluginKind = "RestoreItemActionV2" + + // PluginKindDeleteItemActionV2 represents a delete item action plugin version 2. + PluginKindDeleteItemActionV2 PluginKind = "DeleteItemActionV2" + + // TODO: we may not need this + // PluginKindPluginListerV2 represents a plugin lister plugin version 2. + PluginKindPluginListerV2 PluginKind = "PluginListerV2" +) + // AllPluginKinds contains all the valid plugin kinds that Velero supports, excluding PluginLister because that is not a // kind that a developer would ever need to implement (it's handled by Velero and the Velero plugin library code). func AllPluginKinds() map[string]PluginKind { diff --git a/pkg/plugin/framework/restore_item_action_client.go b/pkg/plugin/framework/restore_item_action_client.go index 33d7fff3a..4ac7bb24b 100644 --- a/pkg/plugin/framework/restore_item_action_client.go +++ b/pkg/plugin/framework/restore_item_action_client.go @@ -27,9 +27,10 @@ import ( proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + restoreitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2" ) -var _ velero.RestoreItemAction = &RestoreItemActionGRPCClient{} +var _ restoreitemactionv2.RestoreItemAction = &RestoreItemActionGRPCClient{} // NewRestoreItemActionPlugin constructs a RestoreItemActionPlugin. func NewRestoreItemActionPlugin(options ...PluginOption) *RestoreItemActionPlugin { @@ -71,7 +72,14 @@ func (c *RestoreItemActionGRPCClient) AppliesTo() (velero.ResourceSelector, erro }, nil } -func (c *RestoreItemActionGRPCClient) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { +func (c *RestoreItemActionGRPCClient) Execute( + input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { + return c.ExecuteV2(context.Background(), input) +} + +func (c *RestoreItemActionGRPCClient) ExecuteV2( + ctx context.Context, input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) { + itemJSON, err := json.Marshal(input.Item.UnstructuredContent()) if err != nil { return nil, errors.WithStack(err) @@ -94,7 +102,7 @@ func (c *RestoreItemActionGRPCClient) Execute(input *velero.RestoreItemActionExe Restore: restoreJSON, } - res, err := c.grpcClient.Execute(context.Background(), req) + res, err := c.grpcClient.Execute(ctx, req) if err != nil { return nil, fromGRPCError(err) } diff --git a/pkg/plugin/framework/restore_item_action_server.go b/pkg/plugin/framework/restore_item_action_server.go index 340559a77..7b2903ede 100644 --- a/pkg/plugin/framework/restore_item_action_server.go +++ b/pkg/plugin/framework/restore_item_action_server.go @@ -26,6 +26,7 @@ import ( api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + restoreitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2" ) // RestoreItemActionGRPCServer implements the proto-generated RestoreItemActionServer interface, and accepts @@ -34,13 +35,13 @@ type RestoreItemActionGRPCServer struct { mux *serverMux } -func (s *RestoreItemActionGRPCServer) getImpl(name string) (velero.RestoreItemAction, error) { +func (s *RestoreItemActionGRPCServer) getImpl(name string) (restoreitemactionv2.RestoreItemAction, error) { impl, err := s.mux.getHandler(name) if err != nil { return nil, err } - itemAction, ok := impl.(velero.RestoreItemAction) + itemAction, ok := impl.(restoreitemactionv2.RestoreItemAction) if !ok { return nil, errors.Errorf("%T is not a restore item action", impl) } @@ -76,7 +77,9 @@ func (s *RestoreItemActionGRPCServer) AppliesTo(ctx context.Context, req *proto. }, nil } -func (s *RestoreItemActionGRPCServer) Execute(ctx context.Context, req *proto.RestoreItemActionExecuteRequest) (response *proto.RestoreItemActionExecuteResponse, err error) { +func (s *RestoreItemActionGRPCServer) Execute( + ctx context.Context, req *proto.RestoreItemActionExecuteRequest) (response *proto.RestoreItemActionExecuteResponse, err error) { + defer func() { if recoveredErr := handlePanic(recover()); recoveredErr != nil { err = recoveredErr @@ -106,11 +109,12 @@ func (s *RestoreItemActionGRPCServer) Execute(ctx context.Context, req *proto.Re return nil, newGRPCError(errors.WithStack(err)) } - executeOutput, err := impl.Execute(&velero.RestoreItemActionExecuteInput{ - Item: &item, - ItemFromBackup: &itemFromBackup, - Restore: &restoreObj, - }) + executeOutput, err := impl.ExecuteV2(ctx, + &velero.RestoreItemActionExecuteInput{ + Item: &item, + ItemFromBackup: &itemFromBackup, + Restore: &restoreObj, + }) if err != nil { return nil, newGRPCError(err) } diff --git a/pkg/plugin/framework/server.go b/pkg/plugin/framework/server.go index f02757e4b..314b084e3 100644 --- a/pkg/plugin/framework/server.go +++ b/pkg/plugin/framework/server.go @@ -74,21 +74,65 @@ type Server interface { // RegisterDeleteItemActions registers multiple Delete item actions. RegisterDeleteItemActions(map[string]HandlerInitializer) Server + // Version 2 + + // RegisterVolumeSnapshottersV2 registers multiple volume snapshotters. + RegisterVolumeSnapshottersV2(map[string]HandlerInitializer) Server + + // RegisterObjectStoreV2 registers an object store. Accepted format + // for the plugin name is /. + RegisterObjectStoreV2(pluginName string, initializer HandlerInitializer) Server + + // RegisterBackupItemActionV2 registers a backup item action. Accepted format + // for the plugin name is /. + RegisterBackupItemActionV2(pluginName string, initializer HandlerInitializer) Server + + // RegisterBackupItemActionsV2 registers multiple backup item actions. + RegisterBackupItemActionsV2(map[string]HandlerInitializer) Server + + // RegisterVolumeSnapshotterV2 registers a volume snapshotter. Accepted format + // for the plugin name is /. + RegisterVolumeSnapshotterV2(pluginName string, initializer HandlerInitializer) Server + + // RegisterObjectStoresV2 registers multiple object stores. + RegisterObjectStoresV2(map[string]HandlerInitializer) Server + + // RegisterRestoreItemActionV2 registers a restore item action. Accepted format + // for the plugin name is /. + RegisterRestoreItemActionV2(pluginName string, initializer HandlerInitializer) Server + + // RegisterRestoreItemActionsV2 registers multiple restore item actions. + RegisterRestoreItemActionsV2(map[string]HandlerInitializer) Server + + // RegisterDeleteItemActionV2 registers a delete item action. Accepted format + // for the plugin name is /. + RegisterDeleteItemActionV2(pluginName string, initializer HandlerInitializer) Server + + // RegisterDeleteItemActionsV2 registers multiple Delete item actions. + RegisterDeleteItemActionsV2(map[string]HandlerInitializer) Server + // Server runs the plugin server. Serve() } // server implements Server. type server struct { - log *logrus.Logger - logLevelFlag *logging.LevelFlag - flagSet *pflag.FlagSet - featureSet *veleroflag.StringArray + log *logrus.Logger + logLevelFlag *logging.LevelFlag + flagSet *pflag.FlagSet + featureSet *veleroflag.StringArray + // Version 1 backupItemAction *BackupItemActionPlugin volumeSnapshotter *VolumeSnapshotterPlugin objectStore *ObjectStorePlugin restoreItemAction *RestoreItemActionPlugin deleteItemAction *DeleteItemActionPlugin + // Version 2 + backupItemActionV2 *BackupItemActionPlugin + volumeSnapshotterV2 *VolumeSnapshotterPlugin + objectStoreV2 *ObjectStorePlugin + restoreItemActionV2 *RestoreItemActionPlugin + deleteItemActionV2 *DeleteItemActionPlugin } // NewServer returns a new Server @@ -177,6 +221,67 @@ func (s *server) RegisterDeleteItemActions(m map[string]HandlerInitializer) Serv return s } +// Version 2 +func (s *server) RegisterBackupItemActionV2(name string, initializer HandlerInitializer) Server { + s.backupItemActionV2.register(name, initializer) + return s +} + +func (s *server) RegisterBackupItemActionsV2(m map[string]HandlerInitializer) Server { + for name := range m { + s.RegisterBackupItemActionV2(name, m[name]) + } + return s +} + +func (s *server) RegisterVolumeSnapshotterV2(name string, initializer HandlerInitializer) Server { + s.volumeSnapshotterV2.register(name, initializer) + return s +} + +func (s *server) RegisterVolumeSnapshottersV2(m map[string]HandlerInitializer) Server { + for name := range m { + s.RegisterVolumeSnapshotterV2(name, m[name]) + } + return s +} + +func (s *server) RegisterObjectStoreV2(name string, initializer HandlerInitializer) Server { + s.objectStoreV2.register(name, initializer) + return s +} + +func (s *server) RegisterObjectStoresV2(m map[string]HandlerInitializer) Server { + for name := range m { + s.RegisterObjectStoreV2(name, m[name]) + } + return s +} + +func (s *server) RegisterRestoreItemActionV2(name string, initializer HandlerInitializer) Server { + s.restoreItemActionV2.register(name, initializer) + return s +} + +func (s *server) RegisterRestoreItemActionsV2(m map[string]HandlerInitializer) Server { + for name := range m { + s.RegisterRestoreItemActionV2(name, m[name]) + } + return s +} + +func (s *server) RegisterDeleteItemActionV2(name string, initializer HandlerInitializer) Server { + s.deleteItemActionV2.register(name, initializer) + return s +} + +func (s *server) RegisterDeleteItemActionsV2(m map[string]HandlerInitializer) Server { + for name := range m { + s.RegisterDeleteItemActionV2(name, m[name]) + } + return s +} + // getNames returns a list of PluginIdentifiers registered with plugin. func getNames(command string, kind PluginKind, plugin Interface) []PluginIdentifier { var pluginIdentifiers []PluginIdentifier @@ -206,6 +311,12 @@ func (s *server) Serve() { pluginIdentifiers = append(pluginIdentifiers, getNames(command, PluginKindObjectStore, s.objectStore)...) pluginIdentifiers = append(pluginIdentifiers, getNames(command, PluginKindRestoreItemAction, s.restoreItemAction)...) pluginIdentifiers = append(pluginIdentifiers, getNames(command, PluginKindDeleteItemAction, s.deleteItemAction)...) + // Version 2 + pluginIdentifiers = append(pluginIdentifiers, getNames(command, PluginKindBackupItemActionV2, s.backupItemActionV2)...) + pluginIdentifiers = append(pluginIdentifiers, getNames(command, PluginKindVolumeSnapshotterV2, s.volumeSnapshotterV2)...) + pluginIdentifiers = append(pluginIdentifiers, getNames(command, PluginKindObjectStoreV2, s.objectStoreV2)...) + pluginIdentifiers = append(pluginIdentifiers, getNames(command, PluginKindRestoreItemActionV2, s.restoreItemActionV2)...) + pluginIdentifiers = append(pluginIdentifiers, getNames(command, PluginKindDeleteItemActionV2, s.deleteItemActionV2)...) pluginLister := NewPluginLister(pluginIdentifiers...) @@ -218,6 +329,14 @@ func (s *server) Serve() { string(PluginKindPluginLister): NewPluginListerPlugin(pluginLister), string(PluginKindRestoreItemAction): s.restoreItemAction, string(PluginKindDeleteItemAction): s.deleteItemAction, + // Version 2 + // TODO: check to see if need pluginLister for V2 + // string(PluginKindPluginLister): NewPluginListerPlugin(pluginLister), + string(PluginKindBackupItemActionV2): s.backupItemActionV2, + string(PluginKindVolumeSnapshotterV2): s.volumeSnapshotterV2, + string(PluginKindObjectStoreV2): s.objectStoreV2, + string(PluginKindRestoreItemActionV2): s.restoreItemActionV2, + string(PluginKindDeleteItemActionV2): s.deleteItemActionV2, }, GRPCServer: plugin.DefaultGRPCServer, }) diff --git a/pkg/plugin/framework/volume_snapshotter_client.go b/pkg/plugin/framework/volume_snapshotter_client.go index 53ac58d25..629b6407c 100644 --- a/pkg/plugin/framework/volume_snapshotter_client.go +++ b/pkg/plugin/framework/volume_snapshotter_client.go @@ -53,12 +53,19 @@ func newVolumeSnapshotterGRPCClient(base *clientBase, clientConn *grpc.ClientCon // configuration key-value pairs. It returns an error if the VolumeSnapshotter // cannot be initialized from the provided config. func (c *VolumeSnapshotterGRPCClient) Init(config map[string]string) error { + return c.InitV2(context.Background(), config) +} + +// InitV2 prepares the VolumeSnapshotter for usage using the provided map of +// configuration key-value pairs. It returns an error if the VolumeSnapshotter +// cannot be initialized from the provided config. +func (c *VolumeSnapshotterGRPCClient) InitV2(ctx context.Context, config map[string]string) error { req := &proto.VolumeSnapshotterInitRequest{ Plugin: c.plugin, Config: config, } - if _, err := c.grpcClient.Init(context.Background(), req); err != nil { + if _, err := c.grpcClient.Init(ctx, req); err != nil { return fromGRPCError(err) } @@ -67,7 +74,14 @@ func (c *VolumeSnapshotterGRPCClient) Init(config map[string]string) error { // CreateVolumeFromSnapshot creates a new block volume, initialized from the provided snapshot, // and with the specified type and IOPS (if using provisioned IOPS). -func (c *VolumeSnapshotterGRPCClient) CreateVolumeFromSnapshot(snapshotID, volumeType, volumeAZ string, iops *int64) (string, error) { +func (c *VolumeSnapshotterGRPCClient) CreateVolumeFromSnapshot( + snapshotID, volumeType, volumeAZ string, iops *int64) (string, error) { + return c.CreateVolumeFromSnapshotV2(context.Background(), snapshotID, volumeType, volumeAZ, iops) +} + +func (c *VolumeSnapshotterGRPCClient) CreateVolumeFromSnapshotV2( + ctx context.Context, snapshotID, volumeType, volumeAZ string, iops *int64) (string, error) { + req := &proto.CreateVolumeRequest{ Plugin: c.plugin, SnapshotID: snapshotID, @@ -81,7 +95,7 @@ func (c *VolumeSnapshotterGRPCClient) CreateVolumeFromSnapshot(snapshotID, volum req.Iops = *iops } - res, err := c.grpcClient.CreateVolumeFromSnapshot(context.Background(), req) + res, err := c.grpcClient.CreateVolumeFromSnapshot(ctx, req) if err != nil { return "", fromGRPCError(err) } @@ -92,13 +106,19 @@ func (c *VolumeSnapshotterGRPCClient) CreateVolumeFromSnapshot(snapshotID, volum // GetVolumeInfo returns the type and IOPS (if using provisioned IOPS) for a specified block // volume. func (c *VolumeSnapshotterGRPCClient) GetVolumeInfo(volumeID, volumeAZ string) (string, *int64, error) { + return c.GetVolumeInfoV2(context.Background(), volumeID, volumeAZ) +} + +func (c *VolumeSnapshotterGRPCClient) GetVolumeInfoV2( + ctx context.Context, volumeID, volumeAZ string) (string, *int64, error) { + req := &proto.GetVolumeInfoRequest{ Plugin: c.plugin, VolumeID: volumeID, VolumeAZ: volumeAZ, } - res, err := c.grpcClient.GetVolumeInfo(context.Background(), req) + res, err := c.grpcClient.GetVolumeInfo(ctx, req) if err != nil { return "", nil, fromGRPCError(err) } @@ -114,6 +134,11 @@ func (c *VolumeSnapshotterGRPCClient) GetVolumeInfo(volumeID, volumeAZ string) ( // CreateSnapshot creates a snapshot of the specified block volume, and applies the provided // set of tags to the snapshot. func (c *VolumeSnapshotterGRPCClient) CreateSnapshot(volumeID, volumeAZ string, tags map[string]string) (string, error) { + return c.CreateSnapshotV2(context.Background(), volumeID, volumeID, tags) +} + +func (c *VolumeSnapshotterGRPCClient) CreateSnapshotV2( + ctx context.Context, volumeID, volumeAZ string, tags map[string]string) (string, error) { req := &proto.CreateSnapshotRequest{ Plugin: c.plugin, VolumeID: volumeID, @@ -121,7 +146,7 @@ func (c *VolumeSnapshotterGRPCClient) CreateSnapshot(volumeID, volumeAZ string, Tags: tags, } - res, err := c.grpcClient.CreateSnapshot(context.Background(), req) + res, err := c.grpcClient.CreateSnapshot(ctx, req) if err != nil { return "", fromGRPCError(err) } @@ -131,12 +156,17 @@ func (c *VolumeSnapshotterGRPCClient) CreateSnapshot(volumeID, volumeAZ string, // DeleteSnapshot deletes the specified volume snapshot. func (c *VolumeSnapshotterGRPCClient) DeleteSnapshot(snapshotID string) error { + return c.DeleteSnapshotV2(context.Background(), snapshotID) +} + +func (c *VolumeSnapshotterGRPCClient) DeleteSnapshotV2( + ctx context.Context, snapshotID string) error { req := &proto.DeleteSnapshotRequest{ Plugin: c.plugin, SnapshotID: snapshotID, } - if _, err := c.grpcClient.DeleteSnapshot(context.Background(), req); err != nil { + if _, err := c.grpcClient.DeleteSnapshot(ctx, req); err != nil { return fromGRPCError(err) } @@ -144,6 +174,11 @@ func (c *VolumeSnapshotterGRPCClient) DeleteSnapshot(snapshotID string) error { } func (c *VolumeSnapshotterGRPCClient) GetVolumeID(pv runtime.Unstructured) (string, error) { + return c.GetVolumeIDV2(context.Background(), pv) +} + +func (c *VolumeSnapshotterGRPCClient) GetVolumeIDV2( + ctx context.Context, pv runtime.Unstructured) (string, error) { encodedPV, err := json.Marshal(pv.UnstructuredContent()) if err != nil { return "", errors.WithStack(err) @@ -154,7 +189,7 @@ func (c *VolumeSnapshotterGRPCClient) GetVolumeID(pv runtime.Unstructured) (stri PersistentVolume: encodedPV, } - resp, err := c.grpcClient.GetVolumeID(context.Background(), req) + resp, err := c.grpcClient.GetVolumeID(ctx, req) if err != nil { return "", fromGRPCError(err) } @@ -163,6 +198,11 @@ func (c *VolumeSnapshotterGRPCClient) GetVolumeID(pv runtime.Unstructured) (stri } func (c *VolumeSnapshotterGRPCClient) SetVolumeID(pv runtime.Unstructured, volumeID string) (runtime.Unstructured, error) { + return c.SetVolumeIDV2(context.Background(), pv, volumeID) +} + +func (c *VolumeSnapshotterGRPCClient) SetVolumeIDV2( + ctx context.Context, pv runtime.Unstructured, volumeID string) (runtime.Unstructured, error) { encodedPV, err := json.Marshal(pv.UnstructuredContent()) if err != nil { return nil, errors.WithStack(err) @@ -174,7 +214,7 @@ func (c *VolumeSnapshotterGRPCClient) SetVolumeID(pv runtime.Unstructured, volum VolumeID: volumeID, } - resp, err := c.grpcClient.SetVolumeID(context.Background(), req) + resp, err := c.grpcClient.SetVolumeID(ctx, req) if err != nil { return nil, fromGRPCError(err) } diff --git a/pkg/plugin/framework/volume_snapshotter_server.go b/pkg/plugin/framework/volume_snapshotter_server.go index bde371c93..40c73ae48 100644 --- a/pkg/plugin/framework/volume_snapshotter_server.go +++ b/pkg/plugin/framework/volume_snapshotter_server.go @@ -24,7 +24,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" + volumesnapshotterv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v2" ) // VolumeSnapshotterGRPCServer implements the proto-generated VolumeSnapshotterServer interface, and accepts @@ -33,13 +33,13 @@ type VolumeSnapshotterGRPCServer struct { mux *serverMux } -func (s *VolumeSnapshotterGRPCServer) getImpl(name string) (velero.VolumeSnapshotter, error) { +func (s *VolumeSnapshotterGRPCServer) getImpl(name string) (volumesnapshotterv2.VolumeSnapshotter, error) { impl, err := s.mux.getHandler(name) if err != nil { return nil, err } - volumeSnapshotter, ok := impl.(velero.VolumeSnapshotter) + volumeSnapshotter, ok := impl.(volumesnapshotterv2.VolumeSnapshotter) if !ok { return nil, errors.Errorf("%T is not a volume snapshotter", impl) } @@ -62,7 +62,7 @@ func (s *VolumeSnapshotterGRPCServer) Init(ctx context.Context, req *proto.Volum return nil, newGRPCError(err) } - if err := impl.Init(req.Config); err != nil { + if err := impl.InitV2(ctx, req.Config); err != nil { return nil, newGRPCError(err) } @@ -92,7 +92,7 @@ func (s *VolumeSnapshotterGRPCServer) CreateVolumeFromSnapshot(ctx context.Conte iops = &req.Iops } - volumeID, err := impl.CreateVolumeFromSnapshot(snapshotID, volumeType, volumeAZ, iops) + volumeID, err := impl.CreateVolumeFromSnapshotV2(ctx, snapshotID, volumeType, volumeAZ, iops) if err != nil { return nil, newGRPCError(err) } @@ -114,7 +114,7 @@ func (s *VolumeSnapshotterGRPCServer) GetVolumeInfo(ctx context.Context, req *pr return nil, newGRPCError(err) } - volumeType, iops, err := impl.GetVolumeInfo(req.VolumeID, req.VolumeAZ) + volumeType, iops, err := impl.GetVolumeInfoV2(ctx, req.VolumeID, req.VolumeAZ) if err != nil { return nil, newGRPCError(err) } @@ -144,7 +144,7 @@ func (s *VolumeSnapshotterGRPCServer) CreateSnapshot(ctx context.Context, req *p return nil, newGRPCError(err) } - snapshotID, err := impl.CreateSnapshot(req.VolumeID, req.VolumeAZ, req.Tags) + snapshotID, err := impl.CreateSnapshotV2(ctx, req.VolumeID, req.VolumeAZ, req.Tags) if err != nil { return nil, newGRPCError(err) } @@ -165,7 +165,7 @@ func (s *VolumeSnapshotterGRPCServer) DeleteSnapshot(ctx context.Context, req *p return nil, newGRPCError(err) } - if err := impl.DeleteSnapshot(req.SnapshotID); err != nil { + if err := impl.DeleteSnapshotV2(ctx, req.SnapshotID); err != nil { return nil, newGRPCError(err) } @@ -190,7 +190,7 @@ func (s *VolumeSnapshotterGRPCServer) GetVolumeID(ctx context.Context, req *prot return nil, newGRPCError(errors.WithStack(err)) } - volumeID, err := impl.GetVolumeID(&pv) + volumeID, err := impl.GetVolumeIDV2(ctx, &pv) if err != nil { return nil, newGRPCError(err) } @@ -215,7 +215,7 @@ func (s *VolumeSnapshotterGRPCServer) SetVolumeID(ctx context.Context, req *prot return nil, newGRPCError(errors.WithStack(err)) } - updatedPV, err := impl.SetVolumeID(&pv, req.VolumeID) + updatedPV, err := impl.SetVolumeIDV2(ctx, &pv, req.VolumeID) if err != nil { return nil, newGRPCError(err) } diff --git a/pkg/plugin/mocks/manager.go b/pkg/plugin/mocks/manager.go index ebf09de96..f3ead6d9a 100644 --- a/pkg/plugin/mocks/manager.go +++ b/pkg/plugin/mocks/manager.go @@ -5,6 +5,7 @@ package mocks import ( mock "github.com/stretchr/testify/mock" velero "github.com/vmware-tanzu/velero/pkg/plugin/velero" + backupitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2" ) // Manager is an autogenerated mock type for the Manager type @@ -18,15 +19,15 @@ func (_m *Manager) CleanupClients() { } // GetBackupItemAction provides a mock function with given fields: name -func (_m *Manager) GetBackupItemAction(name string) (velero.BackupItemAction, error) { +func (_m *Manager) GetBackupItemAction(name string) (backupitemactionv2.BackupItemAction, error) { ret := _m.Called(name) - var r0 velero.BackupItemAction - if rf, ok := ret.Get(0).(func(string) velero.BackupItemAction); ok { + var r0 backupitemactionv2.BackupItemAction + if rf, ok := ret.Get(0).(func(string) backupitemactionv2.BackupItemAction); ok { r0 = rf(name) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(velero.BackupItemAction) + r0 = ret.Get(0).(backupitemactionv2.BackupItemAction) } } @@ -41,15 +42,15 @@ func (_m *Manager) GetBackupItemAction(name string) (velero.BackupItemAction, er } // GetBackupItemActions provides a mock function with given fields: -func (_m *Manager) GetBackupItemActions() ([]velero.BackupItemAction, error) { +func (_m *Manager) GetBackupItemActions() ([]backupitemactionv2.BackupItemAction, error) { ret := _m.Called() - var r0 []velero.BackupItemAction - if rf, ok := ret.Get(0).(func() []velero.BackupItemAction); ok { + var r0 []backupitemactionv2.BackupItemAction + if rf, ok := ret.Get(0).(func() []backupitemactionv2.BackupItemAction); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]velero.BackupItemAction) + r0 = ret.Get(0).([]backupitemactionv2.BackupItemAction) } } diff --git a/pkg/plugin/velero/backup_item_action.go b/pkg/plugin/velero/backupitemaction/v1/backup_item_actionv1.go similarity index 75% rename from pkg/plugin/velero/backup_item_action.go rename to pkg/plugin/velero/backupitemaction/v1/backup_item_actionv1.go index 70d28555f..d7820ade5 100644 --- a/pkg/plugin/velero/backup_item_action.go +++ b/pkg/plugin/velero/backupitemaction/v1/backup_item_actionv1.go @@ -1,5 +1,5 @@ /* -Copyright 2017 the Velero contributors. +Copyright 2021 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. @@ -14,13 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -package velero +package v1 import ( "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) // BackupItemAction is an actor that performs an operation on an individual item being backed up. @@ -28,18 +28,12 @@ type BackupItemAction interface { // AppliesTo returns information about which resources this action should be invoked for. // A BackupItemAction's Execute function will only be invoked on items that match the returned // selector. A zero-valued ResourceSelector matches all resources. - AppliesTo() (ResourceSelector, error) + AppliesTo() (velero.ResourceSelector, error) // Execute allows the ItemAction to perform arbitrary logic with the item being backed up, // including mutating the item itself prior to backup. The item (unmodified or modified) // should be returned, along with an optional slice of ResourceIdentifiers specifying // additional related items that should be backed up. - Execute(item runtime.Unstructured, backup *api.Backup) (runtime.Unstructured, []ResourceIdentifier, error) -} - -// ResourceIdentifier describes a single item by its group, resource, namespace, and name. -type ResourceIdentifier struct { - schema.GroupResource - Namespace string - Name string + Execute(item runtime.Unstructured, backup *api.Backup) ( + runtime.Unstructured, []velero.ResourceIdentifier, error) } diff --git a/pkg/plugin/velero/backupitemaction/v2/backup_item_actionv2.go b/pkg/plugin/velero/backupitemaction/v2/backup_item_actionv2.go new file mode 100644 index 000000000..bcdbfcc40 --- /dev/null +++ b/pkg/plugin/velero/backupitemaction/v2/backup_item_actionv2.go @@ -0,0 +1,38 @@ +/* +Copyright 2021 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 v2 + +import ( + "k8s.io/apimachinery/pkg/runtime" + + api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" + v1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" + + "context" +) + +type BackupItemAction interface { + v1.BackupItemAction + + // ExecuteV2 allows the ItemAction to perform arbitrary logic with the item being backed up, + // including mutating the item itself prior to backup. The item (unmodified or modified) + // should be returned, along with an optional slice of ResourceIdentifiers specifying + // additional related items that should be backed up. + ExecuteV2(ctx context.Context, item runtime.Unstructured, + backup *api.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) +} diff --git a/pkg/plugin/velero/delete_item_action.go b/pkg/plugin/velero/delete_item_action.go index e3e7d9259..2a0e7d418 100644 --- a/pkg/plugin/velero/delete_item_action.go +++ b/pkg/plugin/velero/delete_item_action.go @@ -1,5 +1,5 @@ /* -Copyright 2020 the Velero contributors. +Copyright 2020, 2021 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. @@ -22,20 +22,6 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" ) -// DeleteItemAction is an actor that performs an operation on an individual item being restored. -type DeleteItemAction interface { - // AppliesTo returns information about which resources this action should be invoked for. - // A DeleteItemAction's Execute function will only be invoked on items that match the returned - // selector. A zero-valued ResourceSelector matches all resources. - AppliesTo() (ResourceSelector, error) - - // Execute allows the ItemAction to perform arbitrary logic with the item being deleted. - // An error should be returned if there were problems with the deletion process, but the - // overall deletion process cannot be stopped. - // Returned errors are logged. - Execute(input *DeleteItemActionExecuteInput) error -} - // DeleteItemActionExecuteInput contains the input parameters for the ItemAction's Execute function. type DeleteItemActionExecuteInput struct { // Item is the item taken from the pristine backed up version of resource. diff --git a/pkg/plugin/velero/deleteitemaction/v1/delete_item_actionv1.go b/pkg/plugin/velero/deleteitemaction/v1/delete_item_actionv1.go new file mode 100644 index 000000000..4f57304ad --- /dev/null +++ b/pkg/plugin/velero/deleteitemaction/v1/delete_item_actionv1.go @@ -0,0 +1,35 @@ +/* +Copyright 2021 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 v1 + +import ( + "github.com/vmware-tanzu/velero/pkg/plugin/velero" +) + +// DeleteItemAction is an actor that performs an operation on an individual item being restored. +type DeleteItemAction interface { + // AppliesTo returns information about which resources this action should be invoked for. + // A DeleteItemAction's Execute function will only be invoked on items that match the returned + // selector. A zero-valued ResourceSelector matches all resources. + AppliesTo() (velero.ResourceSelector, error) + + // Execute allows the ItemAction to perform arbitrary logic with the item being deleted. + // An error should be returned if there were problems with the deletion process, but the + // overall deletion process cannot be stopped. + // Returned errors are logged. + Execute(input *velero.DeleteItemActionExecuteInput) error +} diff --git a/pkg/plugin/velero/deleteitemaction/v2/delete_item_actionv2.go b/pkg/plugin/velero/deleteitemaction/v2/delete_item_actionv2.go new file mode 100644 index 000000000..46e721af4 --- /dev/null +++ b/pkg/plugin/velero/deleteitemaction/v2/delete_item_actionv2.go @@ -0,0 +1,34 @@ +/* +Copyright 2021 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 v2 + +import ( + "github.com/vmware-tanzu/velero/pkg/plugin/velero" + v1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/deleteitemaction/v1" + + "context" +) + +type DeleteItemAction interface { + v1.DeleteItemAction + + // ExecuteV2 allows the ItemAction to perform arbitrary logic with the item being deleted. + // An error should be returned if there were problems with the deletion process, but the + // overall deletion process cannot be stopped. + // Returned errors are logged. + ExecuteV2(ctx context.Context, input *velero.DeleteItemActionExecuteInput) error +} diff --git a/pkg/plugin/velero/mocks/DeleteItemAction.go b/pkg/plugin/velero/mocks/DeleteItemAction.go index 101e04fde..94e755ced 100644 --- a/pkg/plugin/velero/mocks/DeleteItemAction.go +++ b/pkg/plugin/velero/mocks/DeleteItemAction.go @@ -4,7 +4,7 @@ package mocks import ( mock "github.com/stretchr/testify/mock" - velero "github.com/vmware-tanzu/velero/pkg/plugin/velero" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" ) // DeleteItemAction is an autogenerated mock type for the DeleteItemAction type @@ -46,3 +46,8 @@ func (_m *DeleteItemAction) Execute(input *velero.DeleteItemActionExecuteInput) return r0 } + +// ExecuteV2 provides a mock function with given fields: ctx, input +func (_m *DeleteItemAction) ExecuteV2(ctx context.Context, input *velero.DeleteItemActionExecuteInput) error { + return _m.Execute(input) +} diff --git a/pkg/plugin/velero/object_store.go b/pkg/plugin/velero/objectstore/v1/object_storev1.go similarity index 99% rename from pkg/plugin/velero/object_store.go rename to pkg/plugin/velero/objectstore/v1/object_storev1.go index 29082e704..28f5d9bc6 100644 --- a/pkg/plugin/velero/object_store.go +++ b/pkg/plugin/velero/objectstore/v1/object_storev1.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package velero +package v1 import ( "io" diff --git a/pkg/plugin/velero/objectstore/v2/object_storev2.go b/pkg/plugin/velero/objectstore/v2/object_storev2.go new file mode 100644 index 000000000..682adf6d3 --- /dev/null +++ b/pkg/plugin/velero/objectstore/v2/object_storev2.go @@ -0,0 +1,68 @@ +/* +Copyright 2021 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 v2 + +import ( + v1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/objectstore/v1" + + "context" + "io" + "time" +) + +type ObjectStore interface { + v1.ObjectStore + + // InitV2 prepares the ObjectStore for usage using the provided map of + // configuration key-value pairs. It returns an error if the ObjectStore + // cannot be initialized from the provided config. + InitV2(ctx context.Context, config map[string]string) error + + // PutObjectV2 creates a new object using the data in body within the specified + // object storage bucket with the given key. + PutObjectV2(ctx context.Context, bucket, key string, body io.Reader) error + + // ObjectExistsV2 checks if there is an object with the given key in the object storage bucket. + ObjectExistsV2(ctx context.Context, bucket, key string) (bool, error) + + // GetObjectV2 retrieves the object with the given key from the specified + // bucket in object storage. + GetObjectV2(ctx context.Context, bucket, key string) (io.ReadCloser, error) + + // ListCommonPrefixesV2 gets a list of all object key prefixes that start with + // the specified prefix and stop at the next instance of the provided delimiter. + // + // For example, if the bucket contains the following keys: + // a-prefix/foo-1/bar + // a-prefix/foo-1/baz + // a-prefix/foo-2/baz + // some-other-prefix/foo-3/bar + // and the provided prefix arg is "a-prefix/", and the delimiter is "/", + // this will return the slice {"a-prefix/foo-1/", "a-prefix/foo-2/"}. + ListCommonPrefixesV2(ctx context.Context, bucket, prefix, delimiter string) ([]string, error) + + // ListObjectsV2 gets a list of all keys in the specified bucket + // that have the given prefix. + ListObjectsV2(ctx context.Context, bucket, prefix string) ([]string, error) + + // DeleteObjectV2 removes the object with the specified key from the given + // bucket. + DeleteObjectV2(ctx context.Context, bucket, key string) error + + // CreateSignedURLV2 creates a pre-signed URL for the given bucket and key that expires after ttl. + CreateSignedURLV2(ctx context.Context, bucket, key string, ttl time.Duration) (string, error) +} diff --git a/pkg/plugin/velero/restore_item_action.go b/pkg/plugin/velero/restore_item_action.go index ea758c93a..4c7b7a15b 100644 --- a/pkg/plugin/velero/restore_item_action.go +++ b/pkg/plugin/velero/restore_item_action.go @@ -1,5 +1,5 @@ /* -Copyright 2017, 2019 the Velero contributors. +Copyright 2017, 2019, 2021 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. @@ -22,22 +22,6 @@ import ( api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" ) -// RestoreItemAction is an actor that performs an operation on an individual item being restored. -type RestoreItemAction interface { - // AppliesTo returns information about which resources this action should be invoked for. - // A RestoreItemAction's Execute function will only be invoked on items that match the returned - // selector. A zero-valued ResourceSelector matches all resources. - AppliesTo() (ResourceSelector, error) - - // Execute allows the ItemAction to perform arbitrary logic with the item being restored, - // including mutating the item itself prior to restore. The item (unmodified or modified) - // should be returned, along with an optional slice of ResourceIdentifiers specifying additional - // related items that should be restored, a warning (which will be logged but will not prevent - // the item from being restored) or error (which will be logged and will prevent the item - // from being restored) if applicable. - Execute(input *RestoreItemActionExecuteInput) (*RestoreItemActionExecuteOutput, error) -} - // RestoreItemActionExecuteInput contains the input parameters for the ItemAction's Execute function. type RestoreItemActionExecuteInput struct { // Item is the item being restored. It is likely different from the pristine backed up version diff --git a/pkg/plugin/velero/restoreitemaction/v1/restore_item_actionv1.go b/pkg/plugin/velero/restoreitemaction/v1/restore_item_actionv1.go new file mode 100644 index 000000000..a110f3b0a --- /dev/null +++ b/pkg/plugin/velero/restoreitemaction/v1/restore_item_actionv1.go @@ -0,0 +1,37 @@ +/* +Copyright 2021 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 v1 + +import ( + "github.com/vmware-tanzu/velero/pkg/plugin/velero" +) + +// RestoreItemAction is an actor that performs an operation on an individual item being restored. +type RestoreItemAction interface { + // AppliesTo returns information about which resources this action should be invoked for. + // A RestoreItemAction's Execute function will only be invoked on items that match the returned + // selector. A zero-valued ResourceSelector matches all resources. + AppliesTo() (velero.ResourceSelector, error) + + // Execute allows the ItemAction to perform arbitrary logic with the item being restored, + // including mutating the item itself prior to restore. The item (unmodified or modified) + // should be returned, along with an optional slice of ResourceIdentifiers specifying additional + // related items that should be restored, a warning (which will be logged but will not prevent + // the item from being restored) or error (which will be logged and will prevent the item + // from being restored) if applicable. + Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) +} diff --git a/pkg/plugin/velero/restoreitemaction/v2/restore_item_actionv2.go b/pkg/plugin/velero/restoreitemaction/v2/restore_item_actionv2.go new file mode 100644 index 000000000..532e485d5 --- /dev/null +++ b/pkg/plugin/velero/restoreitemaction/v2/restore_item_actionv2.go @@ -0,0 +1,37 @@ +/* +Copyright 2021 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 v2 + +import ( + "github.com/vmware-tanzu/velero/pkg/plugin/velero" + v1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" + + "context" +) + +type RestoreItemAction interface { + v1.RestoreItemAction + + // ExecuteV2 allows the ItemAction to perform arbitrary logic with the item being restored, + // including mutating the item itself prior to restore. The item (unmodified or modified) + // should be returned, along with an optional slice of ResourceIdentifiers specifying additional + // related items that should be restored, a warning (which will be logged but will not prevent + // the item from being restored) or error (which will be logged and will prevent the item + // from being restored) if applicable. + ExecuteV2(ctx context.Context, + input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) +} diff --git a/pkg/plugin/velero/shared.go b/pkg/plugin/velero/shared.go index 138de4886..b664e71cb 100644 --- a/pkg/plugin/velero/shared.go +++ b/pkg/plugin/velero/shared.go @@ -1,5 +1,5 @@ /* -Copyright 2019 the Velero contributors. +Copyright 2019, 2021 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. @@ -20,6 +20,10 @@ limitations under the License. // plugins of any type can be implemented. package velero +import ( + "k8s.io/apimachinery/pkg/runtime/schema" +) + // ResourceSelector is a collection of included/excluded namespaces, // included/excluded resources, and a label-selector that can be used // to match a set of items from a cluster. @@ -48,3 +52,10 @@ type ResourceSelector struct { // for details on syntax. LabelSelector string } + +// ResourceIdentifier describes a single item by its group, resource, namespace, and name. +type ResourceIdentifier struct { + schema.GroupResource + Namespace string + Name string +} diff --git a/pkg/plugin/velero/volume_snapshotter.go b/pkg/plugin/velero/volumesnapshotter/v1/volume_snapshotterv1.go similarity index 99% rename from pkg/plugin/velero/volume_snapshotter.go rename to pkg/plugin/velero/volumesnapshotter/v1/volume_snapshotterv1.go index 1a4e38bc0..fe58f06f5 100644 --- a/pkg/plugin/velero/volume_snapshotter.go +++ b/pkg/plugin/velero/volumesnapshotter/v1/volume_snapshotterv1.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package velero +package v1 import ( "k8s.io/apimachinery/pkg/runtime" diff --git a/pkg/plugin/velero/volumesnapshotter/v2/volume_snapshotterv2.go b/pkg/plugin/velero/volumesnapshotter/v2/volume_snapshotterv2.go new file mode 100644 index 000000000..6e57b35a9 --- /dev/null +++ b/pkg/plugin/velero/volumesnapshotter/v2/volume_snapshotterv2.go @@ -0,0 +1,59 @@ +/* +Copyright 2021 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 v2 + +import ( + "k8s.io/apimachinery/pkg/runtime" + + v1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v1" + + "context" +) + +type VolumeSnapshotter interface { + v1.VolumeSnapshotter + + // InitV2 prepares the VolumeSnapshotter for usage using the provided map of + // configuration key-value pairs. It returns an error if the VolumeSnapshotter + // cannot be initialized from the provided config. + InitV2(ctx context.Context, config map[string]string) error + + // CreateVolumeFromSnapshotV2 creates a new volume in the specified + // availability zone, initialized from the provided snapshot, + // and with the specified type and IOPS (if using provisioned IOPS). + CreateVolumeFromSnapshotV2(ctx context.Context, + snapshotID, volumeType, volumeAZ string, iops *int64) (volumeID string, err error) + + // GetVolumeIDV2 returns the cloud provider specific identifier for the PersistentVolume. + GetVolumeIDV2(ctx context.Context, pv runtime.Unstructured) (string, error) + + // SetVolumeIDV2 sets the cloud provider specific identifier for the PersistentVolume. + SetVolumeIDV2(ctx context.Context, + pv runtime.Unstructured, volumeID string) (runtime.Unstructured, error) + + // GetVolumeInfoV2 returns the type and IOPS (if using provisioned IOPS) for + // the specified volume in the given availability zone. + GetVolumeInfoV2(ctx context.Context, volumeID, volumeAZ string) (string, *int64, error) + + // CreateSnapshotV2 creates a snapshot of the specified volume, and applies the provided + // set of tags to the snapshot. + CreateSnapshotV2(ctx context.Context, + volumeID, volumeAZ string, tags map[string]string) (snapshotID string, err error) + + // DeleteSnapshotV2 deletes the specified volume snapshot. + DeleteSnapshotV2(ctx context.Context, snapshotID string) error +} diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index c874dd7c7..a4871f131 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -57,6 +57,8 @@ import ( "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/label" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + restoreitemaction "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2" + volumesnapshotter "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v2" "github.com/vmware-tanzu/velero/pkg/podexec" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/boolptr" @@ -75,7 +77,7 @@ const KubeAnnBoundByController = "pv.kubernetes.io/bound-by-controller" const KubeAnnDynamicallyProvisioned = "pv.kubernetes.io/provisioned-by" type VolumeSnapshotterGetter interface { - GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) + GetVolumeSnapshotter(name string) (volumesnapshotter.VolumeSnapshotter, error) } type Request struct { @@ -92,7 +94,7 @@ type Request struct { type Restorer interface { // Restore restores the backup data from backupReader, returning warnings and errors. Restore(req Request, - actions []velero.RestoreItemAction, + actions []restoreitemaction.RestoreItemAction, snapshotLocationLister listers.VolumeSnapshotLocationLister, volumeSnapshotterGetter VolumeSnapshotterGetter, ) (Result, Result) @@ -158,7 +160,7 @@ func NewKubernetesRestorer( // respectively, summarizing info about the restore. func (kr *kubernetesRestorer) Restore( req Request, - actions []velero.RestoreItemAction, + actions []restoreitemaction.RestoreItemAction, snapshotLocationLister listers.VolumeSnapshotLocationLister, volumeSnapshotterGetter VolumeSnapshotterGetter, ) (Result, Result) { @@ -278,14 +280,14 @@ func (kr *kubernetesRestorer) Restore( } type resolvedAction struct { - velero.RestoreItemAction + restoreitemaction.RestoreItemAction resourceIncludesExcludes *collections.IncludesExcludes namespaceIncludesExcludes *collections.IncludesExcludes selector labels.Selector } -func resolveActions(actions []velero.RestoreItemAction, helper discovery.Helper) ([]resolvedAction, error) { +func resolveActions(actions []restoreitemaction.RestoreItemAction, helper discovery.Helper) ([]resolvedAction, error) { var resolved []resolvedAction for _, action := range actions {