Merge pull request #5569 from sseago/riav2-impl
Restore Item Action v2 API implementationpull/5784/head
commit
cf2b482c97
|
@ -0,0 +1 @@
|
|||
RestoreItemAction v2 API implementation
|
|
@ -474,11 +474,11 @@ func (c *restoreController) runValidatedRestore(restore *api.Restore, info backu
|
|||
pluginManager := c.newPluginManager(restoreLog)
|
||||
defer pluginManager.CleanupClients()
|
||||
|
||||
actions, err := pluginManager.GetRestoreItemActions()
|
||||
actions, err := pluginManager.GetRestoreItemActionsV2()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error getting restore item actions")
|
||||
}
|
||||
actionsResolver := framework.NewRestoreItemActionResolver(actions)
|
||||
actionsResolver := framework.NewRestoreItemActionResolverV2(actions)
|
||||
|
||||
itemSnapshotters, err := pluginManager.GetItemSnapshotters()
|
||||
if err != nil {
|
||||
|
|
|
@ -47,7 +47,7 @@ import (
|
|||
"github.com/vmware-tanzu/velero/pkg/plugin/framework"
|
||||
pluginmocks "github.com/vmware-tanzu/velero/pkg/plugin/mocks"
|
||||
isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1"
|
||||
riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1"
|
||||
riav2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2"
|
||||
pkgrestore "github.com/vmware-tanzu/velero/pkg/restore"
|
||||
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/logging"
|
||||
|
@ -556,7 +556,7 @@ func TestProcessQueueItem(t *testing.T) {
|
|||
}
|
||||
|
||||
if test.restore != nil {
|
||||
pluginManager.On("GetRestoreItemActions").Return(nil, nil)
|
||||
pluginManager.On("GetRestoreItemActionsV2").Return(nil, nil)
|
||||
pluginManager.On("GetItemSnapshotters").Return([]isv1.ItemSnapshotter{}, nil)
|
||||
pluginManager.On("CleanupClients")
|
||||
}
|
||||
|
@ -861,7 +861,7 @@ type fakeRestorer struct {
|
|||
|
||||
func (r *fakeRestorer) Restore(
|
||||
info pkgrestore.Request,
|
||||
actions []riav1.RestoreItemAction,
|
||||
actions []riav2.RestoreItemAction,
|
||||
snapshotLocationLister listers.VolumeSnapshotLocationLister,
|
||||
volumeSnapshotterGetter pkgrestore.VolumeSnapshotterGetter,
|
||||
) (pkgrestore.Result, pkgrestore.Result) {
|
||||
|
@ -873,7 +873,7 @@ func (r *fakeRestorer) Restore(
|
|||
}
|
||||
|
||||
func (r *fakeRestorer) RestoreWithResolvers(req pkgrestore.Request,
|
||||
resolver framework.RestoreItemActionResolver,
|
||||
resolver framework.RestoreItemActionResolverV2,
|
||||
itemSnapshotterResolver framework.ItemSnapshotterResolver,
|
||||
snapshotLocationLister listers.VolumeSnapshotLocationLister,
|
||||
volumeSnapshotterGetter pkgrestore.VolumeSnapshotterGetter,
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
biav2cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/backupitemaction/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process"
|
||||
riav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/restoreitemaction/v1"
|
||||
riav2cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/restoreitemaction/v2"
|
||||
vsv1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/volumesnapshotter/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework/common"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
|
@ -35,6 +36,7 @@ import (
|
|||
biav2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2"
|
||||
isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1"
|
||||
riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1"
|
||||
riav2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2"
|
||||
vsv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v1"
|
||||
)
|
||||
|
||||
|
@ -64,6 +66,12 @@ type Manager interface {
|
|||
// GetRestoreItemAction returns the restore item action plugin for name.
|
||||
GetRestoreItemAction(name string) (riav1.RestoreItemAction, error)
|
||||
|
||||
// GetRestoreItemActionsV2 returns all v2 restore item action plugins.
|
||||
GetRestoreItemActionsV2() ([]riav2.RestoreItemAction, error)
|
||||
|
||||
// GetRestoreItemActionV2 returns the restore item action plugin for name.
|
||||
GetRestoreItemActionV2(name string) (riav2.RestoreItemAction, error)
|
||||
|
||||
// GetDeleteItemActions returns all delete item action plugins.
|
||||
GetDeleteItemActions() ([]velero.DeleteItemAction, error)
|
||||
|
||||
|
@ -302,6 +310,44 @@ func (m *manager) GetRestoreItemAction(name string) (riav1.RestoreItemAction, er
|
|||
return nil, fmt.Errorf("unable to get valid RestoreItemAction for %q", name)
|
||||
}
|
||||
|
||||
// GetRestoreItemActionsV2 returns all v2 restore item actions as restartableRestoreItemActions.
|
||||
func (m *manager) GetRestoreItemActionsV2() ([]riav2.RestoreItemAction, error) {
|
||||
list := m.registry.List(common.PluginKindRestoreItemActionV2)
|
||||
|
||||
actions := make([]riav2.RestoreItemAction, 0, len(list))
|
||||
|
||||
for i := range list {
|
||||
id := list[i]
|
||||
|
||||
r, err := m.GetRestoreItemActionV2(id.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
actions = append(actions, r)
|
||||
}
|
||||
|
||||
return actions, nil
|
||||
}
|
||||
|
||||
// GetRestoreItemActionV2 returns a v2 restartableRestoreItemAction for name.
|
||||
func (m *manager) GetRestoreItemActionV2(name string) (riav2.RestoreItemAction, error) {
|
||||
name = sanitizeName(name)
|
||||
|
||||
for _, adaptedRestoreItemAction := range riav2cli.AdaptedRestoreItemActions() {
|
||||
restartableProcess, err := m.getRestartableProcess(adaptedRestoreItemAction.Kind, name)
|
||||
// Check if plugin was not found
|
||||
if errors.As(err, &pluginNotFoundErrType) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return adaptedRestoreItemAction.GetRestartable(name, restartableProcess), nil
|
||||
}
|
||||
return nil, fmt.Errorf("unable to get valid RestoreItemActionV2 for %q", name)
|
||||
}
|
||||
|
||||
// GetDeleteItemActions returns all delete item actions as restartableDeleteItemActions.
|
||||
func (m *manager) GetDeleteItemActions() ([]velero.DeleteItemAction, error) {
|
||||
list := m.registry.List(common.PluginKindDeleteItemAction)
|
||||
|
|
|
@ -31,6 +31,7 @@ import (
|
|||
biav2cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/backupitemaction/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process"
|
||||
riav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/restoreitemaction/v1"
|
||||
riav2cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/restoreitemaction/v2"
|
||||
vsv1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/volumesnapshotter/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework/common"
|
||||
|
@ -238,6 +239,23 @@ func TestGetRestoreItemAction(t *testing.T) {
|
|||
)
|
||||
}
|
||||
|
||||
func TestGetRestoreItemActionV2(t *testing.T) {
|
||||
getPluginTest(t,
|
||||
common.PluginKindRestoreItemActionV2,
|
||||
"velero.io/pod",
|
||||
func(m Manager, name string) (interface{}, error) {
|
||||
return m.GetRestoreItemActionV2(name)
|
||||
},
|
||||
func(name string, sharedPluginProcess process.RestartableProcess) interface{} {
|
||||
return &riav2cli.RestartableRestoreItemAction{
|
||||
Key: process.KindAndName{Kind: common.PluginKindRestoreItemActionV2, Name: name},
|
||||
SharedPluginProcess: sharedPluginProcess,
|
||||
}
|
||||
},
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
func getPluginTest(
|
||||
t *testing.T,
|
||||
kind common.PluginKind,
|
||||
|
@ -565,6 +583,98 @@ func TestGetRestoreItemActions(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGetRestoreItemActionsV2(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
names []string
|
||||
newRestartableProcessError error
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "No items",
|
||||
names: []string{},
|
||||
},
|
||||
{
|
||||
name: "Error getting restartable process",
|
||||
names: []string{"velero.io/a", "velero.io/b", "velero.io/c"},
|
||||
newRestartableProcessError: errors.Errorf("NewRestartableProcess"),
|
||||
expectedError: "NewRestartableProcess",
|
||||
},
|
||||
{
|
||||
name: "Happy path",
|
||||
names: []string{"velero.io/a", "velero.io/b", "velero.io/c"},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
logger := test.NewLogger()
|
||||
logLevel := logrus.InfoLevel
|
||||
|
||||
registry := &mockRegistry{}
|
||||
defer registry.AssertExpectations(t)
|
||||
|
||||
m := NewManager(logger, logLevel, registry).(*manager)
|
||||
factory := &mockRestartableProcessFactory{}
|
||||
defer factory.AssertExpectations(t)
|
||||
m.restartableProcessFactory = factory
|
||||
|
||||
pluginKind := common.PluginKindRestoreItemActionV2
|
||||
var pluginIDs []framework.PluginIdentifier
|
||||
for i := range tc.names {
|
||||
pluginID := framework.PluginIdentifier{
|
||||
Command: "/command",
|
||||
Kind: pluginKind,
|
||||
Name: tc.names[i],
|
||||
}
|
||||
pluginIDs = append(pluginIDs, pluginID)
|
||||
}
|
||||
registry.On("List", pluginKind).Return(pluginIDs)
|
||||
|
||||
var expectedActions []interface{}
|
||||
for i := range pluginIDs {
|
||||
pluginID := pluginIDs[i]
|
||||
pluginName := pluginID.Name
|
||||
|
||||
registry.On("Get", pluginKind, pluginName).Return(pluginID, nil)
|
||||
|
||||
restartableProcess := &restartabletest.MockRestartableProcess{}
|
||||
defer restartableProcess.AssertExpectations(t)
|
||||
|
||||
expected := &riav2cli.RestartableRestoreItemAction{
|
||||
Key: process.KindAndName{Kind: pluginKind, Name: pluginName},
|
||||
SharedPluginProcess: restartableProcess,
|
||||
}
|
||||
|
||||
if tc.newRestartableProcessError != nil {
|
||||
// Test 1: error getting restartable process
|
||||
factory.On("NewRestartableProcess", pluginID.Command, logger, logLevel).Return(nil, errors.Errorf("NewRestartableProcess")).Once()
|
||||
break
|
||||
}
|
||||
|
||||
// Test 2: happy path
|
||||
if i == 0 {
|
||||
factory.On("NewRestartableProcess", pluginID.Command, logger, logLevel).Return(restartableProcess, nil).Once()
|
||||
}
|
||||
|
||||
expectedActions = append(expectedActions, expected)
|
||||
}
|
||||
|
||||
restoreItemActions, err := m.GetRestoreItemActionsV2()
|
||||
if tc.newRestartableProcessError != nil {
|
||||
assert.Nil(t, restoreItemActions)
|
||||
assert.EqualError(t, err, "NewRestartableProcess")
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
var actual []interface{}
|
||||
for i := range restoreItemActions {
|
||||
actual = append(actual, restoreItemActions[i])
|
||||
}
|
||||
assert.Equal(t, expectedActions, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDeleteItemAction(t *testing.T) {
|
||||
getPluginTest(t,
|
||||
common.PluginKindDeleteItemAction,
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/vmware-tanzu/velero/pkg/plugin/framework"
|
||||
biav2 "github.com/vmware-tanzu/velero/pkg/plugin/framework/backupitemaction/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework/common"
|
||||
riav2 "github.com/vmware-tanzu/velero/pkg/plugin/framework/restoreitemaction/v2"
|
||||
)
|
||||
|
||||
// clientBuilder builds go-plugin Clients.
|
||||
|
@ -69,14 +70,15 @@ func (b *clientBuilder) clientConfig() *hcplugin.ClientConfig {
|
|||
HandshakeConfig: framework.Handshake(),
|
||||
AllowedProtocols: []hcplugin.Protocol{hcplugin.ProtocolGRPC},
|
||||
Plugins: map[string]hcplugin.Plugin{
|
||||
string(common.PluginKindBackupItemAction): framework.NewBackupItemActionPlugin(common.ClientLogger(b.clientLogger)),
|
||||
string(common.PluginKindBackupItemActionV2): biav2.NewBackupItemActionPlugin(common.ClientLogger(b.clientLogger)),
|
||||
string(common.PluginKindVolumeSnapshotter): framework.NewVolumeSnapshotterPlugin(common.ClientLogger(b.clientLogger)),
|
||||
string(common.PluginKindObjectStore): framework.NewObjectStorePlugin(common.ClientLogger(b.clientLogger)),
|
||||
string(common.PluginKindPluginLister): &framework.PluginListerPlugin{},
|
||||
string(common.PluginKindRestoreItemAction): framework.NewRestoreItemActionPlugin(common.ClientLogger(b.clientLogger)),
|
||||
string(common.PluginKindDeleteItemAction): framework.NewDeleteItemActionPlugin(common.ClientLogger(b.clientLogger)),
|
||||
string(common.PluginKindItemSnapshotter): framework.NewItemSnapshotterPlugin(common.ClientLogger(b.clientLogger)),
|
||||
string(common.PluginKindBackupItemAction): framework.NewBackupItemActionPlugin(common.ClientLogger(b.clientLogger)),
|
||||
string(common.PluginKindBackupItemActionV2): biav2.NewBackupItemActionPlugin(common.ClientLogger(b.clientLogger)),
|
||||
string(common.PluginKindVolumeSnapshotter): framework.NewVolumeSnapshotterPlugin(common.ClientLogger(b.clientLogger)),
|
||||
string(common.PluginKindObjectStore): framework.NewObjectStorePlugin(common.ClientLogger(b.clientLogger)),
|
||||
string(common.PluginKindPluginLister): &framework.PluginListerPlugin{},
|
||||
string(common.PluginKindRestoreItemAction): framework.NewRestoreItemActionPlugin(common.ClientLogger(b.clientLogger)),
|
||||
string(common.PluginKindRestoreItemActionV2): riav2.NewRestoreItemActionPlugin(common.ClientLogger(b.clientLogger)),
|
||||
string(common.PluginKindDeleteItemAction): framework.NewDeleteItemActionPlugin(common.ClientLogger(b.clientLogger)),
|
||||
string(common.PluginKindItemSnapshotter): framework.NewItemSnapshotterPlugin(common.ClientLogger(b.clientLogger)),
|
||||
},
|
||||
Logger: b.pluginLogger,
|
||||
Cmd: exec.Command(b.commandName, b.commandArgs...), //nolint
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/vmware-tanzu/velero/pkg/plugin/framework"
|
||||
biav2 "github.com/vmware-tanzu/velero/pkg/plugin/framework/backupitemaction/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework/common"
|
||||
riav2 "github.com/vmware-tanzu/velero/pkg/plugin/framework/restoreitemaction/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/test"
|
||||
)
|
||||
|
||||
|
@ -62,14 +63,15 @@ func TestClientConfig(t *testing.T) {
|
|||
HandshakeConfig: framework.Handshake(),
|
||||
AllowedProtocols: []hcplugin.Protocol{hcplugin.ProtocolGRPC},
|
||||
Plugins: map[string]hcplugin.Plugin{
|
||||
string(common.PluginKindBackupItemAction): framework.NewBackupItemActionPlugin(common.ClientLogger(logger)),
|
||||
string(common.PluginKindBackupItemActionV2): biav2.NewBackupItemActionPlugin(common.ClientLogger(logger)),
|
||||
string(common.PluginKindVolumeSnapshotter): framework.NewVolumeSnapshotterPlugin(common.ClientLogger(logger)),
|
||||
string(common.PluginKindObjectStore): framework.NewObjectStorePlugin(common.ClientLogger(logger)),
|
||||
string(common.PluginKindPluginLister): &framework.PluginListerPlugin{},
|
||||
string(common.PluginKindRestoreItemAction): framework.NewRestoreItemActionPlugin(common.ClientLogger(logger)),
|
||||
string(common.PluginKindDeleteItemAction): framework.NewDeleteItemActionPlugin(common.ClientLogger(logger)),
|
||||
string(common.PluginKindItemSnapshotter): framework.NewItemSnapshotterPlugin(common.ClientLogger(logger)),
|
||||
string(common.PluginKindBackupItemAction): framework.NewBackupItemActionPlugin(common.ClientLogger(logger)),
|
||||
string(common.PluginKindBackupItemActionV2): biav2.NewBackupItemActionPlugin(common.ClientLogger(logger)),
|
||||
string(common.PluginKindVolumeSnapshotter): framework.NewVolumeSnapshotterPlugin(common.ClientLogger(logger)),
|
||||
string(common.PluginKindObjectStore): framework.NewObjectStorePlugin(common.ClientLogger(logger)),
|
||||
string(common.PluginKindPluginLister): &framework.PluginListerPlugin{},
|
||||
string(common.PluginKindRestoreItemAction): framework.NewRestoreItemActionPlugin(common.ClientLogger(logger)),
|
||||
string(common.PluginKindRestoreItemActionV2): riav2.NewRestoreItemActionPlugin(common.ClientLogger(logger)),
|
||||
string(common.PluginKindDeleteItemAction): framework.NewDeleteItemActionPlugin(common.ClientLogger(logger)),
|
||||
string(common.PluginKindItemSnapshotter): framework.NewItemSnapshotterPlugin(common.ClientLogger(logger)),
|
||||
},
|
||||
Logger: cb.pluginLogger,
|
||||
Cmd: exec.Command(cb.commandName, cb.commandArgs...),
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
Copyright the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process"
|
||||
riav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/restoreitemaction/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework/common"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
riav2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2"
|
||||
)
|
||||
|
||||
// AdaptedRestoreItemAction is a v1 RestoreItemAction adapted to implement the v2 API
|
||||
type AdaptedRestoreItemAction struct {
|
||||
Kind common.PluginKind
|
||||
|
||||
// Get returns a restartable RestoreItemAction for the given name and process, wrapping if necessary
|
||||
GetRestartable func(name string, restartableProcess process.RestartableProcess) riav2.RestoreItemAction
|
||||
}
|
||||
|
||||
func AdaptedRestoreItemActions() []AdaptedRestoreItemAction {
|
||||
return []AdaptedRestoreItemAction{
|
||||
{
|
||||
Kind: common.PluginKindRestoreItemActionV2,
|
||||
GetRestartable: func(name string, restartableProcess process.RestartableProcess) riav2.RestoreItemAction {
|
||||
return NewRestartableRestoreItemAction(name, restartableProcess)
|
||||
},
|
||||
},
|
||||
{
|
||||
Kind: common.PluginKindRestoreItemAction,
|
||||
GetRestartable: func(name string, restartableProcess process.RestartableProcess) riav2.RestoreItemAction {
|
||||
return NewAdaptedV1RestartableRestoreItemAction(riav1cli.NewRestartableRestoreItemAction(name, restartableProcess))
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// RestartableRestoreItemAction is a restore item action for a given implementation (such as "pod"). It is associated with
|
||||
// a restartableProcess, which may be shared and used to run multiple plugins. At the beginning of each method
|
||||
// call, the RestartableRestoreItemAction asks its restartableProcess to restart itself if needed (e.g. if the
|
||||
// process terminated for any reason), then it proceeds with the actual call.
|
||||
type RestartableRestoreItemAction struct {
|
||||
Key process.KindAndName
|
||||
SharedPluginProcess process.RestartableProcess
|
||||
config map[string]string
|
||||
}
|
||||
|
||||
// NewRestartableRestoreItemAction returns a new RestartableRestoreItemAction.
|
||||
func NewRestartableRestoreItemAction(name string, sharedPluginProcess process.RestartableProcess) *RestartableRestoreItemAction {
|
||||
r := &RestartableRestoreItemAction{
|
||||
Key: process.KindAndName{Kind: common.PluginKindRestoreItemActionV2, Name: name},
|
||||
SharedPluginProcess: sharedPluginProcess,
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// getRestoreItemAction returns the restore item action for this RestartableRestoreItemAction. It does *not* restart the
|
||||
// plugin process.
|
||||
func (r *RestartableRestoreItemAction) getRestoreItemAction() (riav2.RestoreItemAction, error) {
|
||||
plugin, err := r.SharedPluginProcess.GetByKindAndName(r.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
restoreItemAction, ok := plugin.(riav2.RestoreItemAction)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("%T is not a RestoreItemActionV2!", plugin)
|
||||
}
|
||||
|
||||
return restoreItemAction, nil
|
||||
}
|
||||
|
||||
// getDelegate restarts the plugin process (if needed) and returns the restore item action for this RestartableRestoreItemAction.
|
||||
func (r *RestartableRestoreItemAction) getDelegate() (riav2.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 RestartableRestoreItemAction) 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 *RestartableRestoreItemAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return delegate.Execute(input)
|
||||
}
|
||||
|
||||
// Progress restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *RestartableRestoreItemAction) Progress(operationID string, restore *api.Restore) (velero.OperationProgress, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return velero.OperationProgress{}, err
|
||||
}
|
||||
|
||||
return delegate.Progress(operationID, restore)
|
||||
}
|
||||
|
||||
// Cancel restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *RestartableRestoreItemAction) Cancel(operationID string, restore *api.Restore) error {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return delegate.Cancel(operationID, restore)
|
||||
}
|
||||
|
||||
// AreAdditionalItemsReady restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *RestartableRestoreItemAction) AreAdditionalItemsReady(additionalItems []velero.ResourceIdentifier, restore *api.Restore) (bool, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return delegate.AreAdditionalItemsReady(additionalItems, restore)
|
||||
}
|
||||
|
||||
type AdaptedV1RestartableRestoreItemAction struct {
|
||||
V1Restartable *riav1cli.RestartableRestoreItemAction
|
||||
}
|
||||
|
||||
// NewAdaptedV1RestartableRestoreItemAction returns a new v1 RestartableRestoreItemAction adapted to v2
|
||||
func NewAdaptedV1RestartableRestoreItemAction(v1Restartable *riav1cli.RestartableRestoreItemAction) *AdaptedV1RestartableRestoreItemAction {
|
||||
r := &AdaptedV1RestartableRestoreItemAction{
|
||||
V1Restartable: v1Restartable,
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// AppliesTo delegates to the v1 AppliesTo call.
|
||||
func (r *AdaptedV1RestartableRestoreItemAction) AppliesTo() (velero.ResourceSelector, error) {
|
||||
return r.V1Restartable.AppliesTo()
|
||||
}
|
||||
|
||||
// Execute delegates to the v1 Execute call, returning an empty operationID.
|
||||
func (r *AdaptedV1RestartableRestoreItemAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) {
|
||||
return r.V1Restartable.Execute(input)
|
||||
}
|
||||
|
||||
// Progress returns with an error since v1 plugins will never return an operationID, which means that
|
||||
// any operationID passed in here will be invalid.
|
||||
func (r *AdaptedV1RestartableRestoreItemAction) Progress(operationID string, restore *api.Restore) (velero.OperationProgress, error) {
|
||||
return velero.OperationProgress{}, riav2.AsyncOperationsNotSupportedError()
|
||||
}
|
||||
|
||||
// Cancel just returns without error since v1 plugins don't implement it.
|
||||
func (r *AdaptedV1RestartableRestoreItemAction) Cancel(operationID string, restore *api.Restore) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AreAdditionalItemsReady just returns true since v1 plugins don't wait for items.
|
||||
func (r *AdaptedV1RestartableRestoreItemAction) AreAdditionalItemsReady(additionalItems []velero.ResourceIdentifier, restore *api.Restore) (bool, error) {
|
||||
return true, nil
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
Copyright 2018, 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/vmware-tanzu/velero/internal/restartabletest"
|
||||
v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework/common"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
mocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks/restoreitemaction/v2"
|
||||
)
|
||||
|
||||
func TestRestartableGetRestoreItemAction(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
plugin interface{}
|
||||
getError error
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "error getting by kind and name",
|
||||
getError: errors.Errorf("get error"),
|
||||
expectedError: "get error",
|
||||
},
|
||||
{
|
||||
name: "wrong type",
|
||||
plugin: 3,
|
||||
expectedError: "int is not a RestoreItemActionV2!",
|
||||
},
|
||||
{
|
||||
name: "happy path",
|
||||
plugin: new(mocks.RestoreItemAction),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
p := new(restartabletest.MockRestartableProcess)
|
||||
defer p.AssertExpectations(t)
|
||||
|
||||
name := "pod"
|
||||
key := process.KindAndName{Kind: common.PluginKindRestoreItemActionV2, Name: name}
|
||||
p.On("GetByKindAndName", key).Return(tc.plugin, tc.getError)
|
||||
|
||||
r := NewRestartableRestoreItemAction(name, p)
|
||||
a, err := r.getRestoreItemAction()
|
||||
if tc.expectedError != "" {
|
||||
assert.EqualError(t, err, tc.expectedError)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.plugin, a)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRestartableRestoreItemActionGetDelegate(t *testing.T) {
|
||||
p := new(restartabletest.MockRestartableProcess)
|
||||
defer p.AssertExpectations(t)
|
||||
|
||||
// Reset error
|
||||
p.On("ResetIfNeeded").Return(errors.Errorf("reset error")).Once()
|
||||
name := "pod"
|
||||
r := NewRestartableRestoreItemAction(name, p)
|
||||
a, err := r.getDelegate()
|
||||
assert.Nil(t, a)
|
||||
assert.EqualError(t, err, "reset error")
|
||||
|
||||
// Happy path
|
||||
p.On("ResetIfNeeded").Return(nil)
|
||||
expected := new(mocks.RestoreItemAction)
|
||||
key := process.KindAndName{Kind: common.PluginKindRestoreItemActionV2, Name: name}
|
||||
p.On("GetByKindAndName", key).Return(expected, nil)
|
||||
|
||||
a, err = r.getDelegate()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, a)
|
||||
}
|
||||
|
||||
func TestRestartableRestoreItemActionDelegatedFunctions(t *testing.T) {
|
||||
pv := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"color": "blue",
|
||||
},
|
||||
}
|
||||
|
||||
input := &velero.RestoreItemActionExecuteInput{
|
||||
Item: pv,
|
||||
ItemFromBackup: pv,
|
||||
Restore: new(v1.Restore),
|
||||
}
|
||||
|
||||
output := &velero.RestoreItemActionExecuteOutput{
|
||||
UpdatedItem: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"color": "green",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
r := new(v1.Restore)
|
||||
oid := "operation1"
|
||||
additionalItems := []velero.ResourceIdentifier{}
|
||||
restartabletest.RunRestartableDelegateTests(
|
||||
t,
|
||||
common.PluginKindRestoreItemActionV2,
|
||||
func(key process.KindAndName, p process.RestartableProcess) interface{} {
|
||||
return &RestartableRestoreItemAction{
|
||||
Key: key,
|
||||
SharedPluginProcess: p,
|
||||
}
|
||||
},
|
||||
func() restartabletest.Mockable {
|
||||
return new(mocks.RestoreItemAction)
|
||||
},
|
||||
restartabletest.RestartableDelegateTest{
|
||||
Function: "AppliesTo",
|
||||
Inputs: []interface{}{},
|
||||
ExpectedErrorOutputs: []interface{}{velero.ResourceSelector{}, errors.Errorf("reset error")},
|
||||
ExpectedDelegateOutputs: []interface{}{velero.ResourceSelector{IncludedNamespaces: []string{"a"}}, errors.Errorf("delegate error")},
|
||||
},
|
||||
restartabletest.RestartableDelegateTest{
|
||||
Function: "Execute",
|
||||
Inputs: []interface{}{input},
|
||||
ExpectedErrorOutputs: []interface{}{nil, errors.Errorf("reset error")},
|
||||
ExpectedDelegateOutputs: []interface{}{output, errors.Errorf("delegate error")},
|
||||
},
|
||||
restartabletest.RestartableDelegateTest{
|
||||
Function: "Progress",
|
||||
Inputs: []interface{}{oid, r},
|
||||
ExpectedErrorOutputs: []interface{}{velero.OperationProgress{}, errors.Errorf("reset error")},
|
||||
ExpectedDelegateOutputs: []interface{}{velero.OperationProgress{}, errors.Errorf("delegate error")},
|
||||
},
|
||||
restartabletest.RestartableDelegateTest{
|
||||
Function: "Cancel",
|
||||
Inputs: []interface{}{oid, r},
|
||||
ExpectedErrorOutputs: []interface{}{errors.Errorf("reset error")},
|
||||
ExpectedDelegateOutputs: []interface{}{errors.Errorf("delegate error")},
|
||||
},
|
||||
restartabletest.RestartableDelegateTest{
|
||||
Function: "AreAdditionalItemsReady",
|
||||
Inputs: []interface{}{additionalItems, r},
|
||||
ExpectedErrorOutputs: []interface{}{false, errors.Errorf("reset error")},
|
||||
ExpectedDelegateOutputs: []interface{}{true, errors.Errorf("delegate error")},
|
||||
},
|
||||
)
|
||||
}
|
|
@ -29,6 +29,7 @@ import (
|
|||
biav2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2"
|
||||
isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1"
|
||||
riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1"
|
||||
riav2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/collections"
|
||||
)
|
||||
|
||||
|
@ -128,6 +129,12 @@ func NewRestoreItemActionResolver(actions []riav1.RestoreItemAction) RestoreItem
|
|||
}
|
||||
}
|
||||
|
||||
func NewRestoreItemActionResolverV2(actions []riav2.RestoreItemAction) RestoreItemActionResolverV2 {
|
||||
return RestoreItemActionResolverV2{
|
||||
actions: actions,
|
||||
}
|
||||
}
|
||||
|
||||
func NewDeleteItemActionResolver(actions []velero.DeleteItemAction) DeleteItemActionResolver {
|
||||
return DeleteItemActionResolver{
|
||||
actions: actions,
|
||||
|
@ -199,6 +206,11 @@ type RestoreItemResolvedAction struct {
|
|||
resolvedAction
|
||||
}
|
||||
|
||||
type RestoreItemResolvedActionV2 struct {
|
||||
riav2.RestoreItemAction
|
||||
resolvedAction
|
||||
}
|
||||
|
||||
type RestoreItemActionResolver struct {
|
||||
actions []riav1.RestoreItemAction
|
||||
}
|
||||
|
@ -223,6 +235,30 @@ func (recv RestoreItemActionResolver) ResolveActions(helper discovery.Helper, lo
|
|||
return resolved, nil
|
||||
}
|
||||
|
||||
type RestoreItemActionResolverV2 struct {
|
||||
actions []riav2.RestoreItemAction
|
||||
}
|
||||
|
||||
func (recv RestoreItemActionResolverV2) ResolveActions(helper discovery.Helper, log logrus.FieldLogger) ([]RestoreItemResolvedActionV2, error) {
|
||||
var resolved []RestoreItemResolvedActionV2
|
||||
for _, action := range recv.actions {
|
||||
resources, namespaces, selector, err := resolveAction(helper, action)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := RestoreItemResolvedActionV2{
|
||||
RestoreItemAction: action,
|
||||
resolvedAction: resolvedAction{
|
||||
ResourceIncludesExcludes: resources,
|
||||
NamespaceIncludesExcludes: namespaces,
|
||||
Selector: selector,
|
||||
},
|
||||
}
|
||||
resolved = append(resolved, res)
|
||||
}
|
||||
return resolved, nil
|
||||
}
|
||||
|
||||
type DeleteItemResolvedAction struct {
|
||||
velero.DeleteItemAction
|
||||
resolvedAction
|
||||
|
|
|
@ -41,6 +41,9 @@ const (
|
|||
// PluginKindRestoreItemAction represents a restore item action plugin.
|
||||
PluginKindRestoreItemAction PluginKind = "RestoreItemAction"
|
||||
|
||||
// PluginKindRestoreItemAction represents a v2 restore item action plugin.
|
||||
PluginKindRestoreItemActionV2 PluginKind = "RestoreItemActionV2"
|
||||
|
||||
// PluginKindDeleteItemAction represents a delete item action plugin.
|
||||
PluginKindDeleteItemAction PluginKind = "DeleteItemAction"
|
||||
|
||||
|
@ -55,7 +58,8 @@ const (
|
|||
// The older (adaptable) version is the key, and the value is the full list of newer
|
||||
// plugin kinds that are capable of adapting it.
|
||||
var PluginKindsAdaptableTo = map[PluginKind][]PluginKind{
|
||||
PluginKindBackupItemAction: {PluginKindBackupItemActionV2},
|
||||
PluginKindBackupItemAction: {PluginKindBackupItemActionV2},
|
||||
PluginKindRestoreItemAction: {PluginKindRestoreItemActionV2},
|
||||
}
|
||||
|
||||
// AllPluginKinds contains all the valid plugin kinds that Velero supports, excluding PluginLister because that is not a
|
||||
|
@ -67,6 +71,7 @@ func AllPluginKinds() map[string]PluginKind {
|
|||
allPluginKinds[PluginKindBackupItemAction.String()] = PluginKindBackupItemAction
|
||||
allPluginKinds[PluginKindBackupItemActionV2.String()] = PluginKindBackupItemActionV2
|
||||
allPluginKinds[PluginKindRestoreItemAction.String()] = PluginKindRestoreItemAction
|
||||
allPluginKinds[PluginKindRestoreItemActionV2.String()] = PluginKindRestoreItemActionV2
|
||||
allPluginKinds[PluginKindDeleteItemAction.String()] = PluginKindDeleteItemAction
|
||||
allPluginKinds[PluginKindItemSnapshotter.String()] = PluginKindItemSnapshotter
|
||||
return allPluginKinds
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
Copyright the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework/common"
|
||||
protoriav2 "github.com/vmware-tanzu/velero/pkg/plugin/generated/restoreitemaction/v2"
|
||||
)
|
||||
|
||||
// RestoreItemActionPlugin is an implementation of go-plugin's Plugin
|
||||
// interface with support for gRPC for the restore/ItemAction
|
||||
// interface.
|
||||
type RestoreItemActionPlugin struct {
|
||||
plugin.NetRPCUnsupportedPlugin
|
||||
*common.PluginBase
|
||||
}
|
||||
|
||||
// GRPCClient returns a RestoreItemAction gRPC client.
|
||||
func (p *RestoreItemActionPlugin) GRPCClient(_ context.Context, _ *plugin.GRPCBroker, clientConn *grpc.ClientConn) (interface{}, error) {
|
||||
return common.NewClientDispenser(p.ClientLogger, clientConn, newRestoreItemActionGRPCClient), nil
|
||||
}
|
||||
|
||||
// GRPCServer registers a RestoreItemAction gRPC server.
|
||||
func (p *RestoreItemActionPlugin) GRPCServer(_ *plugin.GRPCBroker, server *grpc.Server) error {
|
||||
protoriav2.RegisterRestoreItemActionServer(server, &RestoreItemActionGRPCServer{mux: p.ServerMux})
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
Copyright the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework/common"
|
||||
protoriav2 "github.com/vmware-tanzu/velero/pkg/plugin/generated/restoreitemaction/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
riav2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2"
|
||||
)
|
||||
|
||||
var _ riav2.RestoreItemAction = &RestoreItemActionGRPCClient{}
|
||||
|
||||
// NewRestoreItemActionPlugin constructs a RestoreItemActionPlugin.
|
||||
func NewRestoreItemActionPlugin(options ...common.PluginOption) *RestoreItemActionPlugin {
|
||||
return &RestoreItemActionPlugin{
|
||||
PluginBase: common.NewPluginBase(options...),
|
||||
}
|
||||
}
|
||||
|
||||
// RestoreItemActionGRPCClient implements the backup/ItemAction interface and uses a
|
||||
// gRPC client to make calls to the plugin server.
|
||||
type RestoreItemActionGRPCClient struct {
|
||||
*common.ClientBase
|
||||
grpcClient protoriav2.RestoreItemActionClient
|
||||
}
|
||||
|
||||
func newRestoreItemActionGRPCClient(base *common.ClientBase, clientConn *grpc.ClientConn) interface{} {
|
||||
return &RestoreItemActionGRPCClient{
|
||||
ClientBase: base,
|
||||
grpcClient: protoriav2.NewRestoreItemActionClient(clientConn),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RestoreItemActionGRPCClient) AppliesTo() (velero.ResourceSelector, error) {
|
||||
res, err := c.grpcClient.AppliesTo(context.Background(), &protoriav2.RestoreItemActionAppliesToRequest{Plugin: c.Plugin})
|
||||
if err != nil {
|
||||
return velero.ResourceSelector{}, common.FromGRPCError(err)
|
||||
}
|
||||
|
||||
if res.ResourceSelector == nil {
|
||||
return velero.ResourceSelector{}, nil
|
||||
}
|
||||
|
||||
return velero.ResourceSelector{
|
||||
IncludedNamespaces: res.ResourceSelector.IncludedNamespaces,
|
||||
ExcludedNamespaces: res.ResourceSelector.ExcludedNamespaces,
|
||||
IncludedResources: res.ResourceSelector.IncludedResources,
|
||||
ExcludedResources: res.ResourceSelector.ExcludedResources,
|
||||
LabelSelector: res.ResourceSelector.Selector,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *RestoreItemActionGRPCClient) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) {
|
||||
itemJSON, err := json.Marshal(input.Item.UnstructuredContent())
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
itemFromBackupJSON, err := json.Marshal(input.ItemFromBackup.UnstructuredContent())
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
restoreJSON, err := json.Marshal(input.Restore)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
req := &protoriav2.RestoreItemActionExecuteRequest{
|
||||
Plugin: c.Plugin,
|
||||
Item: itemJSON,
|
||||
ItemFromBackup: itemFromBackupJSON,
|
||||
Restore: restoreJSON,
|
||||
}
|
||||
|
||||
res, err := c.grpcClient.Execute(context.Background(), req)
|
||||
if err != nil {
|
||||
return nil, common.FromGRPCError(err)
|
||||
}
|
||||
|
||||
var updatedItem unstructured.Unstructured
|
||||
if err := json.Unmarshal(res.Item, &updatedItem); err != nil {
|
||||
return 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 &velero.RestoreItemActionExecuteOutput{
|
||||
UpdatedItem: &updatedItem,
|
||||
AdditionalItems: additionalItems,
|
||||
SkipRestore: res.SkipRestore,
|
||||
OperationID: res.OperationID,
|
||||
WaitForAdditionalItems: res.WaitForAdditionalItems,
|
||||
AdditionalItemsReadyTimeout: res.AdditionalItemsReadyTimeout.AsDuration(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *RestoreItemActionGRPCClient) Progress(operationID string, restore *api.Restore) (velero.OperationProgress, error) {
|
||||
restoreJSON, err := json.Marshal(restore)
|
||||
if err != nil {
|
||||
return velero.OperationProgress{}, errors.WithStack(err)
|
||||
}
|
||||
req := &protoriav2.RestoreItemActionProgressRequest{
|
||||
Plugin: c.Plugin,
|
||||
OperationID: operationID,
|
||||
Restore: restoreJSON,
|
||||
}
|
||||
|
||||
res, err := c.grpcClient.Progress(context.Background(), req)
|
||||
if err != nil {
|
||||
return velero.OperationProgress{}, common.FromGRPCError(err)
|
||||
}
|
||||
|
||||
return velero.OperationProgress{
|
||||
Completed: res.Progress.Completed,
|
||||
Err: res.Progress.Err,
|
||||
NCompleted: res.Progress.NCompleted,
|
||||
NTotal: res.Progress.NTotal,
|
||||
OperationUnits: res.Progress.OperationUnits,
|
||||
Description: res.Progress.Description,
|
||||
Started: res.Progress.Started.AsTime(),
|
||||
Updated: res.Progress.Updated.AsTime(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *RestoreItemActionGRPCClient) Cancel(operationID string, restore *api.Restore) error {
|
||||
restoreJSON, err := json.Marshal(restore)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
req := &protoriav2.RestoreItemActionCancelRequest{
|
||||
Plugin: c.Plugin,
|
||||
OperationID: operationID,
|
||||
Restore: restoreJSON,
|
||||
}
|
||||
|
||||
_, err = c.grpcClient.Cancel(context.Background(), req)
|
||||
if err != nil {
|
||||
return common.FromGRPCError(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *RestoreItemActionGRPCClient) AreAdditionalItemsReady(additionalItems []velero.ResourceIdentifier, restore *api.Restore) (bool, error) {
|
||||
restoreJSON, err := json.Marshal(restore)
|
||||
if err != nil {
|
||||
return false, errors.WithStack(err)
|
||||
}
|
||||
|
||||
req := &protoriav2.RestoreItemActionItemsReadyRequest{
|
||||
Plugin: c.Plugin,
|
||||
Restore: restoreJSON,
|
||||
}
|
||||
for _, item := range additionalItems {
|
||||
req.AdditionalItems = append(req.AdditionalItems, restoreResourceIdentifierToProto(item))
|
||||
}
|
||||
|
||||
res, err := c.grpcClient.AreAdditionalItemsReady(context.Background(), req)
|
||||
if err != nil {
|
||||
return false, common.FromGRPCError(err)
|
||||
}
|
||||
|
||||
return res.Ready, nil
|
||||
}
|
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
Copyright the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework/common"
|
||||
proto "github.com/vmware-tanzu/velero/pkg/plugin/generated"
|
||||
protoriav2 "github.com/vmware-tanzu/velero/pkg/plugin/generated/restoreitemaction/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
riav2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2"
|
||||
)
|
||||
|
||||
// RestoreItemActionGRPCServer implements the proto-generated RestoreItemActionServer interface, and accepts
|
||||
// gRPC calls and forwards them to an implementation of the pluggable interface.
|
||||
type RestoreItemActionGRPCServer struct {
|
||||
mux *common.ServerMux
|
||||
}
|
||||
|
||||
func (s *RestoreItemActionGRPCServer) getImpl(name string) (riav2.RestoreItemAction, error) {
|
||||
impl, err := s.mux.GetHandler(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
itemAction, ok := impl.(riav2.RestoreItemAction)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("%T is not a restore item action (v2)", impl)
|
||||
}
|
||||
|
||||
return itemAction, nil
|
||||
}
|
||||
|
||||
func (s *RestoreItemActionGRPCServer) AppliesTo(ctx context.Context, req *protoriav2.RestoreItemActionAppliesToRequest) (response *protoriav2.RestoreItemActionAppliesToResponse, err error) {
|
||||
defer func() {
|
||||
if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil {
|
||||
err = recoveredErr
|
||||
}
|
||||
}()
|
||||
|
||||
impl, err := s.getImpl(req.Plugin)
|
||||
if err != nil {
|
||||
return nil, common.NewGRPCError(err)
|
||||
}
|
||||
|
||||
resourceSelector, err := impl.AppliesTo()
|
||||
if err != nil {
|
||||
return nil, common.NewGRPCError(err)
|
||||
}
|
||||
|
||||
return &protoriav2.RestoreItemActionAppliesToResponse{
|
||||
ResourceSelector: &proto.ResourceSelector{
|
||||
IncludedNamespaces: resourceSelector.IncludedNamespaces,
|
||||
ExcludedNamespaces: resourceSelector.ExcludedNamespaces,
|
||||
IncludedResources: resourceSelector.IncludedResources,
|
||||
ExcludedResources: resourceSelector.ExcludedResources,
|
||||
Selector: resourceSelector.LabelSelector,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *RestoreItemActionGRPCServer) Execute(ctx context.Context, req *protoriav2.RestoreItemActionExecuteRequest) (response *protoriav2.RestoreItemActionExecuteResponse, err error) {
|
||||
defer func() {
|
||||
if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil {
|
||||
err = recoveredErr
|
||||
}
|
||||
}()
|
||||
|
||||
impl, err := s.getImpl(req.Plugin)
|
||||
if err != nil {
|
||||
return nil, common.NewGRPCError(err)
|
||||
}
|
||||
|
||||
var (
|
||||
item unstructured.Unstructured
|
||||
itemFromBackup unstructured.Unstructured
|
||||
restoreObj api.Restore
|
||||
)
|
||||
|
||||
if err := json.Unmarshal(req.Item, &item); err != nil {
|
||||
return nil, common.NewGRPCError(errors.WithStack(err))
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(req.ItemFromBackup, &itemFromBackup); err != nil {
|
||||
return nil, common.NewGRPCError(errors.WithStack(err))
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(req.Restore, &restoreObj); err != nil {
|
||||
return nil, common.NewGRPCError(errors.WithStack(err))
|
||||
}
|
||||
|
||||
executeOutput, err := impl.Execute(&velero.RestoreItemActionExecuteInput{
|
||||
Item: &item,
|
||||
ItemFromBackup: &itemFromBackup,
|
||||
Restore: &restoreObj,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, common.NewGRPCError(err)
|
||||
}
|
||||
|
||||
// If the plugin implementation returned a nil updateItem (meaning no modifications), reset updatedItem to the
|
||||
// original item.
|
||||
var updatedItemJSON []byte
|
||||
if executeOutput.UpdatedItem == nil {
|
||||
updatedItemJSON = req.Item
|
||||
} else {
|
||||
updatedItemJSON, err = json.Marshal(executeOutput.UpdatedItem.UnstructuredContent())
|
||||
if err != nil {
|
||||
return nil, common.NewGRPCError(errors.WithStack(err))
|
||||
}
|
||||
}
|
||||
|
||||
res := &protoriav2.RestoreItemActionExecuteResponse{
|
||||
Item: updatedItemJSON,
|
||||
SkipRestore: executeOutput.SkipRestore,
|
||||
OperationID: executeOutput.OperationID,
|
||||
WaitForAdditionalItems: executeOutput.WaitForAdditionalItems,
|
||||
AdditionalItemsReadyTimeout: durationpb.New(executeOutput.AdditionalItemsReadyTimeout),
|
||||
}
|
||||
|
||||
for _, item := range executeOutput.AdditionalItems {
|
||||
res.AdditionalItems = append(res.AdditionalItems, restoreResourceIdentifierToProto(item))
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (s *RestoreItemActionGRPCServer) Progress(ctx context.Context, req *protoriav2.RestoreItemActionProgressRequest) (
|
||||
response *protoriav2.RestoreItemActionProgressResponse, err error) {
|
||||
defer func() {
|
||||
if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil {
|
||||
err = recoveredErr
|
||||
}
|
||||
}()
|
||||
|
||||
impl, err := s.getImpl(req.Plugin)
|
||||
if err != nil {
|
||||
return nil, common.NewGRPCError(err)
|
||||
}
|
||||
|
||||
var restore api.Restore
|
||||
if err := json.Unmarshal(req.Restore, &restore); err != nil {
|
||||
return nil, common.NewGRPCError(errors.WithStack(err))
|
||||
}
|
||||
|
||||
progress, err := impl.Progress(req.OperationID, &restore)
|
||||
if err != nil {
|
||||
return nil, common.NewGRPCError(err)
|
||||
}
|
||||
|
||||
res := &protoriav2.RestoreItemActionProgressResponse{
|
||||
Progress: &proto.OperationProgress{
|
||||
Completed: progress.Completed,
|
||||
Err: progress.Err,
|
||||
NCompleted: progress.NCompleted,
|
||||
NTotal: progress.NTotal,
|
||||
OperationUnits: progress.OperationUnits,
|
||||
Description: progress.Description,
|
||||
Started: timestamppb.New(progress.Started),
|
||||
Updated: timestamppb.New(progress.Updated),
|
||||
},
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (s *RestoreItemActionGRPCServer) Cancel(
|
||||
ctx context.Context, req *protoriav2.RestoreItemActionCancelRequest) (
|
||||
response *emptypb.Empty, err error) {
|
||||
defer func() {
|
||||
if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil {
|
||||
err = recoveredErr
|
||||
}
|
||||
}()
|
||||
|
||||
impl, err := s.getImpl(req.Plugin)
|
||||
if err != nil {
|
||||
return nil, common.NewGRPCError(err)
|
||||
}
|
||||
|
||||
var restore api.Restore
|
||||
if err := json.Unmarshal(req.Restore, &restore); err != nil {
|
||||
return nil, common.NewGRPCError(errors.WithStack(err))
|
||||
}
|
||||
|
||||
err = impl.Cancel(req.OperationID, &restore)
|
||||
if err != nil {
|
||||
return nil, common.NewGRPCError(err)
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *RestoreItemActionGRPCServer) AreAdditionalItemsReady(ctx context.Context, req *protoriav2.RestoreItemActionItemsReadyRequest) (
|
||||
response *protoriav2.RestoreItemActionItemsReadyResponse, err error) {
|
||||
defer func() {
|
||||
if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil {
|
||||
err = recoveredErr
|
||||
}
|
||||
}()
|
||||
|
||||
impl, err := s.getImpl(req.Plugin)
|
||||
if err != nil {
|
||||
return nil, common.NewGRPCError(err)
|
||||
}
|
||||
|
||||
var restore api.Restore
|
||||
if err := json.Unmarshal(req.Restore, &restore); err != nil {
|
||||
return nil, common.NewGRPCError(errors.WithStack(err))
|
||||
}
|
||||
var additionalItems []velero.ResourceIdentifier
|
||||
for _, itm := range req.AdditionalItems {
|
||||
newItem := velero.ResourceIdentifier{
|
||||
GroupResource: schema.GroupResource{
|
||||
Group: itm.Group,
|
||||
Resource: itm.Resource,
|
||||
},
|
||||
Namespace: itm.Namespace,
|
||||
Name: itm.Name,
|
||||
}
|
||||
|
||||
additionalItems = append(additionalItems, newItem)
|
||||
}
|
||||
ready, err := impl.AreAdditionalItemsReady(additionalItems, &restore)
|
||||
if err != nil {
|
||||
return nil, common.NewGRPCError(err)
|
||||
}
|
||||
|
||||
res := &protoriav2.RestoreItemActionItemsReadyResponse{
|
||||
Ready: ready,
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func restoreResourceIdentifierToProto(id velero.ResourceIdentifier) *proto.ResourceIdentifier {
|
||||
return &proto.ResourceIdentifier{
|
||||
Group: id.Group,
|
||||
Resource: id.Resource,
|
||||
Namespace: id.Namespace,
|
||||
Name: id.Name,
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ import (
|
|||
|
||||
biav2 "github.com/vmware-tanzu/velero/pkg/plugin/framework/backupitemaction/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework/common"
|
||||
riav2 "github.com/vmware-tanzu/velero/pkg/plugin/framework/restoreitemaction/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/logging"
|
||||
)
|
||||
|
||||
|
@ -75,6 +76,13 @@ type Server interface {
|
|||
// RegisterRestoreItemActions registers multiple restore item actions.
|
||||
RegisterRestoreItemActions(map[string]common.HandlerInitializer) Server
|
||||
|
||||
// RegisterRestoreItemActionV2 registers a v2 restore item action. Accepted format
|
||||
// for the plugin name is <DNS subdomain>/<non-empty name>.
|
||||
RegisterRestoreItemActionV2(pluginName string, initializer common.HandlerInitializer) Server
|
||||
|
||||
// RegisterRestoreItemActionsV2 registers multiple v2 restore item actions.
|
||||
RegisterRestoreItemActionsV2(map[string]common.HandlerInitializer) Server
|
||||
|
||||
// RegisterDeleteItemAction registers a delete item action. Accepted format
|
||||
// for the plugin name is <DNS subdomain>/<non-empty name>.
|
||||
RegisterDeleteItemAction(pluginName string, initializer common.HandlerInitializer) Server
|
||||
|
@ -93,16 +101,17 @@ type Server interface {
|
|||
|
||||
// server implements Server.
|
||||
type server struct {
|
||||
log *logrus.Logger
|
||||
logLevelFlag *logging.LevelFlag
|
||||
flagSet *pflag.FlagSet
|
||||
backupItemAction *BackupItemActionPlugin
|
||||
backupItemActionV2 *biav2.BackupItemActionPlugin
|
||||
volumeSnapshotter *VolumeSnapshotterPlugin
|
||||
objectStore *ObjectStorePlugin
|
||||
restoreItemAction *RestoreItemActionPlugin
|
||||
deleteItemAction *DeleteItemActionPlugin
|
||||
itemSnapshotter *ItemSnapshotterPlugin
|
||||
log *logrus.Logger
|
||||
logLevelFlag *logging.LevelFlag
|
||||
flagSet *pflag.FlagSet
|
||||
backupItemAction *BackupItemActionPlugin
|
||||
backupItemActionV2 *biav2.BackupItemActionPlugin
|
||||
volumeSnapshotter *VolumeSnapshotterPlugin
|
||||
objectStore *ObjectStorePlugin
|
||||
restoreItemAction *RestoreItemActionPlugin
|
||||
restoreItemActionV2 *riav2.RestoreItemActionPlugin
|
||||
deleteItemAction *DeleteItemActionPlugin
|
||||
itemSnapshotter *ItemSnapshotterPlugin
|
||||
}
|
||||
|
||||
// NewServer returns a new Server
|
||||
|
@ -110,15 +119,16 @@ func NewServer() Server {
|
|||
log := newLogger()
|
||||
|
||||
return &server{
|
||||
log: log,
|
||||
logLevelFlag: logging.LogLevelFlag(log.Level),
|
||||
backupItemAction: NewBackupItemActionPlugin(common.ServerLogger(log)),
|
||||
backupItemActionV2: biav2.NewBackupItemActionPlugin(common.ServerLogger(log)),
|
||||
volumeSnapshotter: NewVolumeSnapshotterPlugin(common.ServerLogger(log)),
|
||||
objectStore: NewObjectStorePlugin(common.ServerLogger(log)),
|
||||
restoreItemAction: NewRestoreItemActionPlugin(common.ServerLogger(log)),
|
||||
deleteItemAction: NewDeleteItemActionPlugin(common.ServerLogger(log)),
|
||||
itemSnapshotter: NewItemSnapshotterPlugin(common.ServerLogger(log)),
|
||||
log: log,
|
||||
logLevelFlag: logging.LogLevelFlag(log.Level),
|
||||
backupItemAction: NewBackupItemActionPlugin(common.ServerLogger(log)),
|
||||
backupItemActionV2: biav2.NewBackupItemActionPlugin(common.ServerLogger(log)),
|
||||
volumeSnapshotter: NewVolumeSnapshotterPlugin(common.ServerLogger(log)),
|
||||
objectStore: NewObjectStorePlugin(common.ServerLogger(log)),
|
||||
restoreItemAction: NewRestoreItemActionPlugin(common.ServerLogger(log)),
|
||||
restoreItemActionV2: riav2.NewRestoreItemActionPlugin(common.ServerLogger(log)),
|
||||
deleteItemAction: NewDeleteItemActionPlugin(common.ServerLogger(log)),
|
||||
itemSnapshotter: NewItemSnapshotterPlugin(common.ServerLogger(log)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,6 +200,18 @@ func (s *server) RegisterRestoreItemActions(m map[string]common.HandlerInitializ
|
|||
return s
|
||||
}
|
||||
|
||||
func (s *server) RegisterRestoreItemActionV2(name string, initializer common.HandlerInitializer) Server {
|
||||
s.restoreItemActionV2.Register(name, initializer)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *server) RegisterRestoreItemActionsV2(m map[string]common.HandlerInitializer) Server {
|
||||
for name := range m {
|
||||
s.RegisterRestoreItemActionV2(name, m[name])
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *server) RegisterDeleteItemAction(name string, initializer common.HandlerInitializer) Server {
|
||||
s.deleteItemAction.Register(name, initializer)
|
||||
return s
|
||||
|
@ -242,6 +264,7 @@ func (s *server) Serve() {
|
|||
pluginIdentifiers = append(pluginIdentifiers, getNames(command, common.PluginKindVolumeSnapshotter, s.volumeSnapshotter)...)
|
||||
pluginIdentifiers = append(pluginIdentifiers, getNames(command, common.PluginKindObjectStore, s.objectStore)...)
|
||||
pluginIdentifiers = append(pluginIdentifiers, getNames(command, common.PluginKindRestoreItemAction, s.restoreItemAction)...)
|
||||
pluginIdentifiers = append(pluginIdentifiers, getNames(command, common.PluginKindRestoreItemActionV2, s.restoreItemActionV2)...)
|
||||
pluginIdentifiers = append(pluginIdentifiers, getNames(command, common.PluginKindDeleteItemAction, s.deleteItemAction)...)
|
||||
pluginIdentifiers = append(pluginIdentifiers, getNames(command, common.PluginKindItemSnapshotter, s.itemSnapshotter)...)
|
||||
|
||||
|
@ -250,14 +273,15 @@ func (s *server) Serve() {
|
|||
plugin.Serve(&plugin.ServeConfig{
|
||||
HandshakeConfig: Handshake(),
|
||||
Plugins: map[string]plugin.Plugin{
|
||||
string(common.PluginKindBackupItemAction): s.backupItemAction,
|
||||
string(common.PluginKindBackupItemActionV2): s.backupItemActionV2,
|
||||
string(common.PluginKindVolumeSnapshotter): s.volumeSnapshotter,
|
||||
string(common.PluginKindObjectStore): s.objectStore,
|
||||
string(common.PluginKindPluginLister): NewPluginListerPlugin(pluginLister),
|
||||
string(common.PluginKindRestoreItemAction): s.restoreItemAction,
|
||||
string(common.PluginKindDeleteItemAction): s.deleteItemAction,
|
||||
string(common.PluginKindItemSnapshotter): s.itemSnapshotter,
|
||||
string(common.PluginKindBackupItemAction): s.backupItemAction,
|
||||
string(common.PluginKindBackupItemActionV2): s.backupItemActionV2,
|
||||
string(common.PluginKindVolumeSnapshotter): s.volumeSnapshotter,
|
||||
string(common.PluginKindObjectStore): s.objectStore,
|
||||
string(common.PluginKindPluginLister): NewPluginListerPlugin(pluginLister),
|
||||
string(common.PluginKindRestoreItemAction): s.restoreItemAction,
|
||||
string(common.PluginKindRestoreItemActionV2): s.restoreItemActionV2,
|
||||
string(common.PluginKindDeleteItemAction): s.deleteItemAction,
|
||||
string(common.PluginKindItemSnapshotter): s.itemSnapshotter,
|
||||
},
|
||||
GRPCServer: plugin.DefaultGRPCServer,
|
||||
})
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -8,6 +8,8 @@ import (
|
|||
|
||||
restoreitemactionv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1"
|
||||
|
||||
restoreitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2"
|
||||
|
||||
v1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1"
|
||||
|
||||
v2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2"
|
||||
|
@ -257,6 +259,29 @@ func (_m *Manager) GetRestoreItemAction(name string) (restoreitemactionv1.Restor
|
|||
return r0, r1
|
||||
}
|
||||
|
||||
// GetRestoreItemActionV2 provides a mock function with given fields: name
|
||||
func (_m *Manager) GetRestoreItemActionV2(name string) (restoreitemactionv2.RestoreItemAction, error) {
|
||||
ret := _m.Called(name)
|
||||
|
||||
var r0 restoreitemactionv2.RestoreItemAction
|
||||
if rf, ok := ret.Get(0).(func(string) restoreitemactionv2.RestoreItemAction); ok {
|
||||
r0 = rf(name)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(restoreitemactionv2.RestoreItemAction)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(name)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetRestoreItemActions provides a mock function with given fields:
|
||||
func (_m *Manager) GetRestoreItemActions() ([]restoreitemactionv1.RestoreItemAction, error) {
|
||||
ret := _m.Called()
|
||||
|
@ -280,6 +305,29 @@ func (_m *Manager) GetRestoreItemActions() ([]restoreitemactionv1.RestoreItemAct
|
|||
return r0, r1
|
||||
}
|
||||
|
||||
// GetRestoreItemActionsV2 provides a mock function with given fields:
|
||||
func (_m *Manager) GetRestoreItemActionsV2() ([]restoreitemactionv2.RestoreItemAction, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 []restoreitemactionv2.RestoreItemAction
|
||||
if rf, ok := ret.Get(0).(func() []restoreitemactionv2.RestoreItemAction); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]restoreitemactionv2.RestoreItemAction)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetVolumeSnapshotter provides a mock function with given fields: name
|
||||
func (_m *Manager) GetVolumeSnapshotter(name string) (volumesnapshotterv1.VolumeSnapshotter, error) {
|
||||
ret := _m.Called(name)
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
syntax = "proto3";
|
||||
package v2;
|
||||
option go_package = "github.com/vmware-tanzu/velero/pkg/plugin/generated/restoreitemaction/v2";
|
||||
|
||||
import "Shared.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/duration.proto";
|
||||
|
||||
message RestoreItemActionExecuteRequest {
|
||||
string plugin = 1;
|
||||
bytes item = 2;
|
||||
bytes restore = 3;
|
||||
bytes itemFromBackup = 4;
|
||||
}
|
||||
|
||||
message RestoreItemActionExecuteResponse {
|
||||
bytes item = 1;
|
||||
repeated generated.ResourceIdentifier additionalItems = 2;
|
||||
bool skipRestore = 3;
|
||||
string operationID = 4;
|
||||
bool waitForAdditionalItems = 5;
|
||||
google.protobuf.Duration additionalItemsReadyTimeout = 6;
|
||||
}
|
||||
|
||||
service RestoreItemAction {
|
||||
rpc AppliesTo(RestoreItemActionAppliesToRequest) returns (RestoreItemActionAppliesToResponse);
|
||||
rpc Execute(RestoreItemActionExecuteRequest) returns (RestoreItemActionExecuteResponse);
|
||||
rpc Progress(RestoreItemActionProgressRequest) returns (RestoreItemActionProgressResponse);
|
||||
rpc Cancel(RestoreItemActionCancelRequest) returns (google.protobuf.Empty);
|
||||
rpc AreAdditionalItemsReady(RestoreItemActionItemsReadyRequest) returns (RestoreItemActionItemsReadyResponse);
|
||||
}
|
||||
|
||||
message RestoreItemActionAppliesToRequest {
|
||||
string plugin = 1;
|
||||
}
|
||||
|
||||
message RestoreItemActionAppliesToResponse {
|
||||
generated.ResourceSelector ResourceSelector = 1;
|
||||
}
|
||||
|
||||
message RestoreItemActionProgressRequest {
|
||||
string plugin = 1;
|
||||
string operationID = 2;
|
||||
bytes restore = 3;
|
||||
}
|
||||
message RestoreItemActionProgressResponse {
|
||||
generated.OperationProgress progress = 1;
|
||||
}
|
||||
message RestoreItemActionCancelRequest {
|
||||
string plugin = 1;
|
||||
string operationID = 2;
|
||||
bytes restore = 3;
|
||||
}
|
||||
message RestoreItemActionItemsReadyRequest {
|
||||
string plugin = 1;
|
||||
bytes restore = 2;
|
||||
repeated generated.ResourceIdentifier additionalItems = 3;
|
||||
}
|
||||
message RestoreItemActionItemsReadyResponse {
|
||||
bool ready = 1;
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
Copyright the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
|
||||
velero "github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
)
|
||||
|
||||
// RestoreItemAction is an autogenerated mock type for the RestoreItemAction type
|
||||
type RestoreItemAction struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// AppliesTo provides a mock function with given fields:
|
||||
func (_m *RestoreItemAction) AppliesTo() (velero.ResourceSelector, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 velero.ResourceSelector
|
||||
if rf, ok := ret.Get(0).(func() velero.ResourceSelector); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(velero.ResourceSelector)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AreAdditionalItemsReady provides a mock function with given fields: AdditionalItems, restore
|
||||
func (_m *RestoreItemAction) AreAdditionalItemsReady(AdditionalItems []velero.ResourceIdentifier, restore *v1.Restore) (bool, error) {
|
||||
ret := _m.Called(AdditionalItems, restore)
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func([]velero.ResourceIdentifier, *v1.Restore) bool); ok {
|
||||
r0 = rf(AdditionalItems, restore)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func([]velero.ResourceIdentifier, *v1.Restore) error); ok {
|
||||
r1 = rf(AdditionalItems, restore)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Cancel provides a mock function with given fields: operationID, restore
|
||||
func (_m *RestoreItemAction) Cancel(operationID string, restore *v1.Restore) error {
|
||||
ret := _m.Called(operationID, restore)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, *v1.Restore) error); ok {
|
||||
r0 = rf(operationID, restore)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Execute provides a mock function with given fields: input
|
||||
func (_m *RestoreItemAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) {
|
||||
ret := _m.Called(input)
|
||||
|
||||
var r0 *velero.RestoreItemActionExecuteOutput
|
||||
if rf, ok := ret.Get(0).(func(*velero.RestoreItemActionExecuteInput) *velero.RestoreItemActionExecuteOutput); ok {
|
||||
r0 = rf(input)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*velero.RestoreItemActionExecuteOutput)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*velero.RestoreItemActionExecuteInput) error); ok {
|
||||
r1 = rf(input)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Progress provides a mock function with given fields: operationID, restore
|
||||
func (_m *RestoreItemAction) Progress(operationID string, restore *v1.Restore) (velero.OperationProgress, error) {
|
||||
ret := _m.Called(operationID, restore)
|
||||
|
||||
var r0 velero.OperationProgress
|
||||
if rf, ok := ret.Get(0).(func(string, *v1.Restore) velero.OperationProgress); ok {
|
||||
r0 = rf(operationID, restore)
|
||||
} else {
|
||||
r0 = ret.Get(0).(velero.OperationProgress)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string, *v1.Restore) error); ok {
|
||||
r1 = rf(operationID, restore)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
|
@ -17,6 +17,8 @@ limitations under the License.
|
|||
package velero
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
|
@ -46,6 +48,26 @@ type RestoreItemActionExecuteOutput struct {
|
|||
// on this item, and skip the restore step. When this field's
|
||||
// value is true, AdditionalItems will be ignored.
|
||||
SkipRestore bool
|
||||
|
||||
// v2 and later
|
||||
// OperationID is an identifier which indicates an ongoing asynchronous action which Velero will
|
||||
// continue to monitor after restoring this item. If left blank, then there is no ongoing operation.
|
||||
OperationID string
|
||||
|
||||
// v2 and later
|
||||
// WaitForAdditionalItems determines whether velero will wait
|
||||
// until AreAdditionalItemsReady returns true before restoring
|
||||
// this item. If this field's value is true, then after restoring
|
||||
// the returned AdditionalItems, velero will not restore this item
|
||||
// until AreAdditionalItemsReady returns true or the timeout is
|
||||
// reached. Otherwise, AreAdditionalItemsReady is not called.
|
||||
WaitForAdditionalItems bool
|
||||
|
||||
// v2 and later
|
||||
// AdditionalItemsReadyTimeout will override serverConfig.additionalItemsReadyTimeout
|
||||
// if specified. This value specifies how long velero will wait
|
||||
// for additional items to be ready before moving on.
|
||||
AdditionalItemsReadyTimeout time.Duration
|
||||
}
|
||||
|
||||
// NewRestoreItemActionExecuteOutput creates a new RestoreItemActionExecuteOutput
|
||||
|
@ -60,3 +82,15 @@ func (r *RestoreItemActionExecuteOutput) WithoutRestore() *RestoreItemActionExec
|
|||
r.SkipRestore = true
|
||||
return r
|
||||
}
|
||||
|
||||
// WithOperationID returns RestoreItemActionExecuteOutput with OperationID set.
|
||||
func (r *RestoreItemActionExecuteOutput) WithOperationID(operationID string) *RestoreItemActionExecuteOutput {
|
||||
r.OperationID = operationID
|
||||
return r
|
||||
}
|
||||
|
||||
// WithItemsWait returns RestoreItemActionExecuteOutput with WaitForAdditionalItems set to true.
|
||||
func (r *RestoreItemActionExecuteOutput) WithItemsWait() *RestoreItemActionExecuteOutput {
|
||||
r.WaitForAdditionalItems = true
|
||||
return r
|
||||
}
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
Copyright the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"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 return struct includes:
|
||||
// The item (unmodified or modified), an optional slice of ResourceIdentifiers
|
||||
// specifying additional related items that should be restored, an optional OperationID,
|
||||
// a bool (waitForAdditionalItems) specifying whether Velero should wait until restored additional
|
||||
// items are ready before restoring this resource, and an optional timeout for the additional items
|
||||
// wait period. An error is returned if the action fails.
|
||||
Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error)
|
||||
|
||||
// Progress allows the RestoreItemAction to report on progress of an asynchronous action.
|
||||
// For the passed-in operation, the plugin will return an OperationProgress struct, indicating
|
||||
// whether the operation has completed, whether there were any errors, a plugin-specific
|
||||
// indication of how much of the operation is done (items completed out of items-to-complete),
|
||||
// and started/updated timestamps
|
||||
Progress(operationID string, restore *api.Restore) (velero.OperationProgress, error)
|
||||
|
||||
// Cancel allows the RestoreItemAction to cancel an asynchronous action (if possible).
|
||||
// Velero will call this if the wait timeout for asynchronous actions has been reached.
|
||||
// If operation cancel is not supported, then the plugin just needs to return. No error
|
||||
// return is expected in this case, since cancellation is optional here.
|
||||
Cancel(operationID string, restore *api.Restore) error
|
||||
|
||||
// AreAdditionalItemsReady allows the ItemAction to communicate whether the passed-in
|
||||
// slice of AdditionalItems (previously returned by Execute())
|
||||
// are ready. Returns true if all items are ready, and false
|
||||
// otherwise. The second return value is to report errors
|
||||
AreAdditionalItemsReady(AdditionalItems []velero.ResourceIdentifier, restore *api.Restore) (bool, error)
|
||||
}
|
||||
|
||||
func AsyncOperationsNotSupportedError() error {
|
||||
return errors.New("Plugin does not support asynchronous operations")
|
||||
}
|
||||
|
||||
func InvalidOperationIDError(operationID string) error {
|
||||
return errors.New(fmt.Sprintf("Operation ID %v is invalid.", operationID))
|
||||
}
|
|
@ -59,7 +59,7 @@ import (
|
|||
"github.com/vmware-tanzu/velero/pkg/label"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1"
|
||||
riav2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2"
|
||||
vsv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/podexec"
|
||||
"github.com/vmware-tanzu/velero/pkg/podvolume"
|
||||
|
@ -88,13 +88,13 @@ type Request struct {
|
|||
type Restorer interface {
|
||||
// Restore restores the backup data from backupReader, returning warnings and errors.
|
||||
Restore(req Request,
|
||||
actions []riav1.RestoreItemAction,
|
||||
actions []riav2.RestoreItemAction,
|
||||
snapshotLocationLister listers.VolumeSnapshotLocationLister,
|
||||
volumeSnapshotterGetter VolumeSnapshotterGetter,
|
||||
) (Result, Result)
|
||||
RestoreWithResolvers(
|
||||
req Request,
|
||||
restoreItemActionResolver framework.RestoreItemActionResolver,
|
||||
restoreItemActionResolver framework.RestoreItemActionResolverV2,
|
||||
itemSnapshotterResolver framework.ItemSnapshotterResolver,
|
||||
snapshotLocationLister listers.VolumeSnapshotLocationLister,
|
||||
volumeSnapshotterGetter VolumeSnapshotterGetter,
|
||||
|
@ -164,18 +164,18 @@ func NewKubernetesRestorer(
|
|||
// respectively, summarizing info about the restore.
|
||||
func (kr *kubernetesRestorer) Restore(
|
||||
req Request,
|
||||
actions []riav1.RestoreItemAction,
|
||||
actions []riav2.RestoreItemAction,
|
||||
snapshotLocationLister listers.VolumeSnapshotLocationLister,
|
||||
volumeSnapshotterGetter VolumeSnapshotterGetter,
|
||||
) (Result, Result) {
|
||||
resolver := framework.NewRestoreItemActionResolver(actions)
|
||||
resolver := framework.NewRestoreItemActionResolverV2(actions)
|
||||
snapshotItemResolver := framework.NewItemSnapshotterResolver(nil)
|
||||
return kr.RestoreWithResolvers(req, resolver, snapshotItemResolver, snapshotLocationLister, volumeSnapshotterGetter)
|
||||
}
|
||||
|
||||
func (kr *kubernetesRestorer) RestoreWithResolvers(
|
||||
req Request,
|
||||
restoreItemActionResolver framework.RestoreItemActionResolver,
|
||||
restoreItemActionResolver framework.RestoreItemActionResolverV2,
|
||||
itemSnapshotterResolver framework.ItemSnapshotterResolver,
|
||||
snapshotLocationLister listers.VolumeSnapshotLocationLister,
|
||||
volumeSnapshotterGetter VolumeSnapshotterGetter,
|
||||
|
@ -342,7 +342,7 @@ type restoreContext struct {
|
|||
dynamicFactory client.DynamicFactory
|
||||
fileSystem filesystem.Interface
|
||||
namespaceClient corev1.NamespaceInterface
|
||||
restoreItemActions []framework.RestoreItemResolvedAction
|
||||
restoreItemActions []framework.RestoreItemResolvedActionV2
|
||||
itemSnapshotterActions []framework.ItemSnapshotterResolvedAction
|
||||
volumeSnapshotterGetter VolumeSnapshotterGetter
|
||||
podVolumeRestorer podvolume.Restorer
|
||||
|
@ -743,8 +743,8 @@ func getNamespace(logger logrus.FieldLogger, path, remappedName string) *v1.Name
|
|||
}
|
||||
}
|
||||
|
||||
func (ctx *restoreContext) getApplicableActions(groupResource schema.GroupResource, namespace string) []framework.RestoreItemResolvedAction {
|
||||
var actions []framework.RestoreItemResolvedAction
|
||||
func (ctx *restoreContext) getApplicableActions(groupResource schema.GroupResource, namespace string) []framework.RestoreItemResolvedActionV2 {
|
||||
var actions []framework.RestoreItemResolvedActionV2
|
||||
for _, action := range ctx.restoreItemActions {
|
||||
if action.ShouldUse(groupResource, namespace, nil, ctx.log) {
|
||||
actions = append(actions, action)
|
||||
|
|
|
@ -48,7 +48,7 @@ import (
|
|||
velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions"
|
||||
"github.com/vmware-tanzu/velero/pkg/kuberesource"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1"
|
||||
riav2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2"
|
||||
vsv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/podvolume"
|
||||
uploadermocks "github.com/vmware-tanzu/velero/pkg/podvolume/mocks"
|
||||
|
@ -1142,9 +1142,12 @@ func TestRestoreItems(t *testing.T) {
|
|||
// to run for specific resources/namespaces and simply records the items
|
||||
// that it is executed for.
|
||||
type recordResourcesAction struct {
|
||||
selector velero.ResourceSelector
|
||||
ids []string
|
||||
additionalItems []velero.ResourceIdentifier
|
||||
selector velero.ResourceSelector
|
||||
ids []string
|
||||
additionalItems []velero.ResourceIdentifier
|
||||
operationID string
|
||||
waitForAdditionalItems bool
|
||||
additionalItemsReadyTimeout time.Duration
|
||||
}
|
||||
|
||||
func (a *recordResourcesAction) AppliesTo() (velero.ResourceSelector, error) {
|
||||
|
@ -1155,18 +1158,36 @@ func (a *recordResourcesAction) Execute(input *velero.RestoreItemActionExecuteIn
|
|||
metadata, err := meta.Accessor(input.Item)
|
||||
if err != nil {
|
||||
return &velero.RestoreItemActionExecuteOutput{
|
||||
UpdatedItem: input.Item,
|
||||
AdditionalItems: a.additionalItems,
|
||||
UpdatedItem: input.Item,
|
||||
AdditionalItems: a.additionalItems,
|
||||
OperationID: a.operationID,
|
||||
WaitForAdditionalItems: a.waitForAdditionalItems,
|
||||
AdditionalItemsReadyTimeout: a.additionalItemsReadyTimeout,
|
||||
}, err
|
||||
}
|
||||
a.ids = append(a.ids, kubeutil.NamespaceAndName(metadata))
|
||||
|
||||
return &velero.RestoreItemActionExecuteOutput{
|
||||
UpdatedItem: input.Item,
|
||||
AdditionalItems: a.additionalItems,
|
||||
UpdatedItem: input.Item,
|
||||
AdditionalItems: a.additionalItems,
|
||||
OperationID: a.operationID,
|
||||
WaitForAdditionalItems: a.waitForAdditionalItems,
|
||||
AdditionalItemsReadyTimeout: a.additionalItemsReadyTimeout,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *recordResourcesAction) Progress(operationID string, restore *velerov1api.Restore) (velero.OperationProgress, error) {
|
||||
return velero.OperationProgress{}, nil
|
||||
}
|
||||
|
||||
func (a *recordResourcesAction) Cancel(operationID string, restore *velerov1api.Restore) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *recordResourcesAction) AreAdditionalItemsReady(additionalItems []velero.ResourceIdentifier, restore *velerov1api.Restore) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (a *recordResourcesAction) ForResource(resource string) *recordResourcesAction {
|
||||
a.selector.IncludedResources = append(a.selector.IncludedResources, resource)
|
||||
return a
|
||||
|
@ -1322,7 +1343,7 @@ func TestRestoreActionsRunForCorrectItems(t *testing.T) {
|
|||
h.AddItems(t, r)
|
||||
}
|
||||
|
||||
actions := []riav1.RestoreItemAction{}
|
||||
actions := []riav2.RestoreItemAction{}
|
||||
for action := range tc.actions {
|
||||
actions = append(actions, action)
|
||||
}
|
||||
|
@ -1374,11 +1395,23 @@ func (a *pluggableAction) AppliesTo() (velero.ResourceSelector, error) {
|
|||
return a.selector, nil
|
||||
}
|
||||
|
||||
func (a *pluggableAction) Progress(operationID string, restore *velerov1api.Restore) (velero.OperationProgress, error) {
|
||||
return velero.OperationProgress{}, nil
|
||||
}
|
||||
|
||||
func (a *pluggableAction) Cancel(operationID string, restore *velerov1api.Restore) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *pluggableAction) addSelector(selector velero.ResourceSelector) *pluggableAction {
|
||||
a.selector = selector
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *pluggableAction) AreAdditionalItemsReady(additionalItems []velero.ResourceIdentifier, restore *velerov1api.Restore) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// TestRestoreActionModifications runs restores with restore item actions that modify resources, and
|
||||
// verifies that that the modified item is correctly created in the API. Verification is done by looking
|
||||
// at the full object in the API.
|
||||
|
@ -1409,7 +1442,7 @@ func TestRestoreActionModifications(t *testing.T) {
|
|||
backup *velerov1api.Backup
|
||||
apiResources []*test.APIResource
|
||||
tarball io.Reader
|
||||
actions []riav1.RestoreItemAction
|
||||
actions []riav2.RestoreItemAction
|
||||
want []*test.APIResource
|
||||
}{
|
||||
{
|
||||
|
@ -1418,7 +1451,7 @@ func TestRestoreActionModifications(t *testing.T) {
|
|||
backup: defaultBackup().Result(),
|
||||
tarball: test.NewTarWriter(t).AddItems("pods", builder.ForPod("ns-1", "pod-1").Result()).Done(),
|
||||
apiResources: []*test.APIResource{test.Pods()},
|
||||
actions: []riav1.RestoreItemAction{
|
||||
actions: []riav2.RestoreItemAction{
|
||||
modifyingActionGetter(func(item *unstructured.Unstructured) {
|
||||
item.SetLabels(map[string]string{"updated": "true"})
|
||||
}),
|
||||
|
@ -1435,7 +1468,7 @@ func TestRestoreActionModifications(t *testing.T) {
|
|||
backup: defaultBackup().Result(),
|
||||
tarball: test.NewTarWriter(t).AddItems("pods", builder.ForPod("ns-1", "pod-1").ObjectMeta(builder.WithLabels("should-be-removed", "true")).Result()).Done(),
|
||||
apiResources: []*test.APIResource{test.Pods()},
|
||||
actions: []riav1.RestoreItemAction{
|
||||
actions: []riav2.RestoreItemAction{
|
||||
modifyingActionGetter(func(item *unstructured.Unstructured) {
|
||||
item.SetLabels(nil)
|
||||
}),
|
||||
|
@ -1450,7 +1483,7 @@ func TestRestoreActionModifications(t *testing.T) {
|
|||
backup: defaultBackup().Result(),
|
||||
tarball: test.NewTarWriter(t).AddItems("pods", builder.ForPod("ns-1", "pod-1").Result()).Done(),
|
||||
apiResources: []*test.APIResource{test.Pods()},
|
||||
actions: []riav1.RestoreItemAction{
|
||||
actions: []riav2.RestoreItemAction{
|
||||
modifyingActionGetter(func(item *unstructured.Unstructured) {
|
||||
item.SetLabels(map[string]string{"updated": "true"})
|
||||
}).addSelector(velero.ResourceSelector{
|
||||
|
@ -1522,7 +1555,7 @@ func TestRestoreActionAdditionalItems(t *testing.T) {
|
|||
backup *velerov1api.Backup
|
||||
tarball io.Reader
|
||||
apiResources []*test.APIResource
|
||||
actions []riav1.RestoreItemAction
|
||||
actions []riav2.RestoreItemAction
|
||||
want map[*test.APIResource][]string
|
||||
}{
|
||||
{
|
||||
|
@ -1531,7 +1564,7 @@ func TestRestoreActionAdditionalItems(t *testing.T) {
|
|||
backup: defaultBackup().Result(),
|
||||
tarball: test.NewTarWriter(t).AddItems("pods", builder.ForPod("ns-1", "pod-1").Result(), builder.ForPod("ns-2", "pod-2").Result()).Done(),
|
||||
apiResources: []*test.APIResource{test.Pods()},
|
||||
actions: []riav1.RestoreItemAction{
|
||||
actions: []riav2.RestoreItemAction{
|
||||
&pluggableAction{
|
||||
selector: velero.ResourceSelector{IncludedNamespaces: []string{"ns-1"}},
|
||||
executeFunc: func(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) {
|
||||
|
@ -1554,7 +1587,7 @@ func TestRestoreActionAdditionalItems(t *testing.T) {
|
|||
backup: defaultBackup().Result(),
|
||||
tarball: test.NewTarWriter(t).AddItems("pods", builder.ForPod("ns-1", "pod-1").Result(), builder.ForPod("ns-2", "pod-2").Result()).Done(),
|
||||
apiResources: []*test.APIResource{test.Pods()},
|
||||
actions: []riav1.RestoreItemAction{
|
||||
actions: []riav2.RestoreItemAction{
|
||||
&pluggableAction{
|
||||
executeFunc: func(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) {
|
||||
return &velero.RestoreItemActionExecuteOutput{
|
||||
|
@ -1579,7 +1612,7 @@ func TestRestoreActionAdditionalItems(t *testing.T) {
|
|||
AddItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result()).
|
||||
Done(),
|
||||
apiResources: []*test.APIResource{test.Pods(), test.PVs()},
|
||||
actions: []riav1.RestoreItemAction{
|
||||
actions: []riav2.RestoreItemAction{
|
||||
&pluggableAction{
|
||||
executeFunc: func(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) {
|
||||
return &velero.RestoreItemActionExecuteOutput{
|
||||
|
@ -1605,7 +1638,7 @@ func TestRestoreActionAdditionalItems(t *testing.T) {
|
|||
AddItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result()).
|
||||
Done(),
|
||||
apiResources: []*test.APIResource{test.Pods(), test.PVs()},
|
||||
actions: []riav1.RestoreItemAction{
|
||||
actions: []riav2.RestoreItemAction{
|
||||
&pluggableAction{
|
||||
executeFunc: func(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) {
|
||||
return &velero.RestoreItemActionExecuteOutput{
|
||||
|
@ -1631,7 +1664,7 @@ func TestRestoreActionAdditionalItems(t *testing.T) {
|
|||
AddItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result()).
|
||||
Done(),
|
||||
apiResources: []*test.APIResource{test.Pods(), test.PVs()},
|
||||
actions: []riav1.RestoreItemAction{
|
||||
actions: []riav2.RestoreItemAction{
|
||||
&pluggableAction{
|
||||
executeFunc: func(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) {
|
||||
return &velero.RestoreItemActionExecuteOutput{
|
||||
|
|
Loading…
Reference in New Issue