mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-01-07 13:55:20 +00:00
Create new ItemBlockAction (IBA) plugin type
Signed-off-by: Scott Seago <sseago@redhat.com>
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
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 v1
|
||||
|
||||
import (
|
||||
"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/clientmgmt/process"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework/common"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
ibav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/itemblockaction/v1"
|
||||
)
|
||||
|
||||
// AdaptedItemBlockAction is an ItemBlock action adapted to the v1 ItemBlockAction API
|
||||
type AdaptedItemBlockAction struct {
|
||||
Kind common.PluginKind
|
||||
|
||||
// Get returns a restartable ItemBlockAction for the given name and process, wrapping if necessary
|
||||
GetRestartable func(name string, restartableProcess process.RestartableProcess) ibav1.ItemBlockAction
|
||||
}
|
||||
|
||||
func AdaptedItemBlockActions() []AdaptedItemBlockAction {
|
||||
return []AdaptedItemBlockAction{
|
||||
{
|
||||
Kind: common.PluginKindItemBlockAction,
|
||||
GetRestartable: func(name string, restartableProcess process.RestartableProcess) ibav1.ItemBlockAction {
|
||||
return NewRestartableItemBlockAction(name, restartableProcess)
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// RestartableItemBlockAction is an ItemBlock 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 restartableItemBlockAction 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 RestartableItemBlockAction struct {
|
||||
Key process.KindAndName
|
||||
SharedPluginProcess process.RestartableProcess
|
||||
}
|
||||
|
||||
// NewRestartableItemBlockAction returns a new RestartableItemBlockAction.
|
||||
func NewRestartableItemBlockAction(name string, sharedPluginProcess process.RestartableProcess) *RestartableItemBlockAction {
|
||||
r := &RestartableItemBlockAction{
|
||||
Key: process.KindAndName{Kind: common.PluginKindItemBlockAction, Name: name},
|
||||
SharedPluginProcess: sharedPluginProcess,
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// getItemBlockAction returns the ItemBlock action for this restartableItemBlockAction. It does *not* restart the
|
||||
// plugin process.
|
||||
func (r *RestartableItemBlockAction) getItemBlockAction() (ibav1.ItemBlockAction, error) {
|
||||
plugin, err := r.SharedPluginProcess.GetByKindAndName(r.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
itemBlockAction, ok := plugin.(ibav1.ItemBlockAction)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("plugin %T is not an ItemBlockAction", plugin)
|
||||
}
|
||||
|
||||
return itemBlockAction, nil
|
||||
}
|
||||
|
||||
// getDelegate restarts the plugin process (if needed) and returns the ItemBlock action for this restartableItemBlockAction.
|
||||
func (r *RestartableItemBlockAction) getDelegate() (ibav1.ItemBlockAction, error) {
|
||||
if err := r.SharedPluginProcess.ResetIfNeeded(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.getItemBlockAction()
|
||||
}
|
||||
|
||||
// Name returns the plugin's name.
|
||||
func (r *RestartableItemBlockAction) Name() string {
|
||||
return r.Key.Name
|
||||
}
|
||||
|
||||
// AppliesTo restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *RestartableItemBlockAction) AppliesTo() (velero.ResourceSelector, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return velero.ResourceSelector{}, err
|
||||
}
|
||||
|
||||
return delegate.AppliesTo()
|
||||
}
|
||||
|
||||
// GetRelatedItems restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *RestartableItemBlockAction) GetRelatedItems(item runtime.Unstructured, backup *api.Backup) ([]velero.ResourceIdentifier, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return delegate.GetRelatedItems(item, backup)
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
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 v1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"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/itemblockaction/v1"
|
||||
)
|
||||
|
||||
func TestRestartableGetItemBlockAction(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: "plugin int is not an ItemBlockAction",
|
||||
},
|
||||
{
|
||||
name: "happy path",
|
||||
plugin: new(mocks.ItemBlockAction),
|
||||
},
|
||||
}
|
||||
|
||||
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.PluginKindItemBlockAction, Name: name}
|
||||
p.On("GetByKindAndName", key).Return(tc.plugin, tc.getError)
|
||||
|
||||
r := NewRestartableItemBlockAction(name, p)
|
||||
a, err := r.getItemBlockAction()
|
||||
if tc.expectedError != "" {
|
||||
assert.EqualError(t, err, tc.expectedError)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.plugin, a)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRestartableItemBlockActionGetDelegate(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 := NewRestartableItemBlockAction(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.ItemBlockAction)
|
||||
key := process.KindAndName{Kind: common.PluginKindItemBlockAction, Name: name}
|
||||
p.On("GetByKindAndName", key).Return(expected, nil)
|
||||
|
||||
a, err = r.getDelegate()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, a)
|
||||
}
|
||||
|
||||
func TestRestartableItemBlockActionDelegatedFunctions(t *testing.T) {
|
||||
b := new(v1.Backup)
|
||||
|
||||
pv := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"color": "blue",
|
||||
},
|
||||
}
|
||||
|
||||
relatedItems := []velero.ResourceIdentifier{
|
||||
{
|
||||
GroupResource: schema.GroupResource{Group: "velero.io", Resource: "backups"},
|
||||
},
|
||||
}
|
||||
|
||||
restartabletest.RunRestartableDelegateTests(
|
||||
t,
|
||||
common.PluginKindItemBlockAction,
|
||||
func(key process.KindAndName, p process.RestartableProcess) interface{} {
|
||||
return &RestartableItemBlockAction{
|
||||
Key: key,
|
||||
SharedPluginProcess: p,
|
||||
}
|
||||
},
|
||||
func() restartabletest.Mockable {
|
||||
return new(mocks.ItemBlockAction)
|
||||
},
|
||||
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: "GetRelatedItems",
|
||||
Inputs: []interface{}{pv, b},
|
||||
ExpectedErrorOutputs: []interface{}{([]velero.ResourceIdentifier)(nil), errors.Errorf("reset error")},
|
||||
ExpectedDelegateOutputs: []interface{}{relatedItems, errors.Errorf("delegate error")},
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
|
||||
biav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/backupitemaction/v1"
|
||||
biav2cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/backupitemaction/v2"
|
||||
ibav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/itemblockaction/v1"
|
||||
"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"
|
||||
@@ -34,6 +35,7 @@ import (
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1"
|
||||
biav2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2"
|
||||
ibav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/itemblockaction/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"
|
||||
@@ -77,6 +79,12 @@ type Manager interface {
|
||||
// GetDeleteItemAction returns the delete item action plugin for name.
|
||||
GetDeleteItemAction(name string) (velero.DeleteItemAction, error)
|
||||
|
||||
// GetItemBlockActions returns all v1 ItemBlock action plugins.
|
||||
GetItemBlockActions() ([]ibav1.ItemBlockAction, error)
|
||||
|
||||
// GetItemBlockAction returns the ItemBlock action plugin for name.
|
||||
GetItemBlockAction(name string) (ibav1.ItemBlockAction, error)
|
||||
|
||||
// CleanupClients terminates all of the Manager's running plugin processes.
|
||||
CleanupClients()
|
||||
}
|
||||
@@ -374,6 +382,44 @@ func (m *manager) GetDeleteItemAction(name string) (velero.DeleteItemAction, err
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// GetItemBlockActions returns all ItemBlock actions as restartableItemBlockActions.
|
||||
func (m *manager) GetItemBlockActions() ([]ibav1.ItemBlockAction, error) {
|
||||
list := m.registry.List(common.PluginKindItemBlockAction)
|
||||
|
||||
actions := make([]ibav1.ItemBlockAction, 0, len(list))
|
||||
|
||||
for i := range list {
|
||||
id := list[i]
|
||||
|
||||
r, err := m.GetItemBlockAction(id.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
actions = append(actions, r)
|
||||
}
|
||||
|
||||
return actions, nil
|
||||
}
|
||||
|
||||
// GetItemBlockAction returns a restartableItemBlockAction for name.
|
||||
func (m *manager) GetItemBlockAction(name string) (ibav1.ItemBlockAction, error) {
|
||||
name = sanitizeName(name)
|
||||
|
||||
for _, adaptedItemBlockAction := range ibav1cli.AdaptedItemBlockActions() {
|
||||
restartableProcess, err := m.getRestartableProcess(adaptedItemBlockAction.Kind, name)
|
||||
// Check if plugin was not found
|
||||
if errors.As(err, &pluginNotFoundErrType) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return adaptedItemBlockAction.GetRestartable(name, restartableProcess), nil
|
||||
}
|
||||
return nil, fmt.Errorf("unable to get valid ItemBlockAction for %q", name)
|
||||
}
|
||||
|
||||
// sanitizeName adds "velero.io" to legacy plugins that weren't namespaced.
|
||||
func sanitizeName(name string) string {
|
||||
// Backwards compatibility with non-namespaced Velero plugins, following principle of least surprise
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/vmware-tanzu/velero/internal/restartabletest"
|
||||
biav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/backupitemaction/v1"
|
||||
biav2cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/backupitemaction/v2"
|
||||
ibav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/itemblockaction/v1"
|
||||
"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"
|
||||
@@ -256,6 +257,23 @@ func TestGetRestoreItemActionV2(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
func TestGetItemBlockAction(t *testing.T) {
|
||||
getPluginTest(t,
|
||||
common.PluginKindItemBlockAction,
|
||||
"velero.io/pod",
|
||||
func(m Manager, name string) (interface{}, error) {
|
||||
return m.GetItemBlockAction(name)
|
||||
},
|
||||
func(name string, sharedPluginProcess process.RestartableProcess) interface{} {
|
||||
return &ibav1cli.RestartableItemBlockAction{
|
||||
Key: process.KindAndName{Kind: common.PluginKindItemBlockAction, Name: name},
|
||||
SharedPluginProcess: sharedPluginProcess,
|
||||
}
|
||||
},
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
func getPluginTest(
|
||||
t *testing.T,
|
||||
kind common.PluginKind,
|
||||
@@ -784,6 +802,98 @@ func TestGetDeleteItemActions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetItemBlockActions(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.PluginKindItemBlockAction
|
||||
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 := &ibav1cli.RestartableItemBlockAction{
|
||||
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)
|
||||
}
|
||||
|
||||
itemBlockActions, err := m.GetItemBlockActions()
|
||||
if tc.newRestartableProcessError != nil {
|
||||
assert.Nil(t, itemBlockActions)
|
||||
assert.EqualError(t, err, "NewRestartableProcess")
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
var actual []interface{}
|
||||
for i := range itemBlockActions {
|
||||
actual = append(actual, itemBlockActions[i])
|
||||
}
|
||||
assert.Equal(t, expectedActions, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSanitizeName(t *testing.T) {
|
||||
tests := []struct {
|
||||
name, pluginName, expectedName string
|
||||
|
||||
@@ -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"
|
||||
ibav1 "github.com/vmware-tanzu/velero/pkg/plugin/framework/itemblockaction/v1"
|
||||
riav2 "github.com/vmware-tanzu/velero/pkg/plugin/framework/restoreitemaction/v2"
|
||||
)
|
||||
|
||||
@@ -78,6 +79,7 @@ func (b *clientBuilder) clientConfig() *hcplugin.ClientConfig {
|
||||
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.PluginKindItemBlockAction): ibav1.NewItemBlockActionPlugin(common.ClientLogger(b.clientLogger)),
|
||||
},
|
||||
Logger: b.pluginLogger,
|
||||
Cmd: exec.Command(b.commandName, b.commandArgs...), //nolint:gosec // Internal call. No need to check the command line.
|
||||
|
||||
@@ -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"
|
||||
ibav1 "github.com/vmware-tanzu/velero/pkg/plugin/framework/itemblockaction/v1"
|
||||
riav2 "github.com/vmware-tanzu/velero/pkg/plugin/framework/restoreitemaction/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/test"
|
||||
)
|
||||
@@ -70,6 +71,7 @@ func TestClientConfig(t *testing.T) {
|
||||
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.PluginKindItemBlockAction): ibav1.NewItemBlockActionPlugin(common.ClientLogger(logger)),
|
||||
},
|
||||
Logger: cb.pluginLogger,
|
||||
Cmd: exec.Command(cb.commandName, cb.commandArgs...),
|
||||
|
||||
Reference in New Issue
Block a user