diff --git a/changelogs/unreleased/8026-sseago b/changelogs/unreleased/8026-sseago new file mode 100644 index 0000000000..a1f1c08973 --- /dev/null +++ b/changelogs/unreleased/8026-sseago @@ -0,0 +1 @@ +Created new ItemBlockAction (IBA) plugin type diff --git a/design/backup-performance-improvements.md b/design/backup-performance-improvements.md index 2d90a7189f..9105a78802 100644 --- a/design/backup-performance-improvements.md +++ b/design/backup-performance-improvements.md @@ -91,13 +91,13 @@ message ItemBlockActionAppliesToResponse { ResourceSelector ResourceSelector = 1; } -message ItemBlockActionRelatedItemsRequest { +message ItemBlockActionGetRelatedItemsRequest { string plugin = 1; bytes item = 2; bytes backup = 3; } -message ItemBlockActionRelatedItemsResponse { +message ItemBlockActionGetRelatedItemsResponse { repeated generated.ResourceIdentifier relatedItems = 1; } ``` diff --git a/pkg/plugin/clientmgmt/itemblockaction/v1/restartable_item_block_action.go b/pkg/plugin/clientmgmt/itemblockaction/v1/restartable_item_block_action.go new file mode 100644 index 0000000000..83742978f1 --- /dev/null +++ b/pkg/plugin/clientmgmt/itemblockaction/v1/restartable_item_block_action.go @@ -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) +} diff --git a/pkg/plugin/clientmgmt/itemblockaction/v1/restartable_item_block_action_test.go b/pkg/plugin/clientmgmt/itemblockaction/v1/restartable_item_block_action_test.go new file mode 100644 index 0000000000..eeec9c74c5 --- /dev/null +++ b/pkg/plugin/clientmgmt/itemblockaction/v1/restartable_item_block_action_test.go @@ -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")}, + }, + ) +} diff --git a/pkg/plugin/clientmgmt/manager.go b/pkg/plugin/clientmgmt/manager.go index aafb398a0c..1c895d86e3 100644 --- a/pkg/plugin/clientmgmt/manager.go +++ b/pkg/plugin/clientmgmt/manager.go @@ -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 diff --git a/pkg/plugin/clientmgmt/manager_test.go b/pkg/plugin/clientmgmt/manager_test.go index 816fef6c02..6919fc9c36 100644 --- a/pkg/plugin/clientmgmt/manager_test.go +++ b/pkg/plugin/clientmgmt/manager_test.go @@ -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 diff --git a/pkg/plugin/clientmgmt/process/client_builder.go b/pkg/plugin/clientmgmt/process/client_builder.go index d414457796..1ae3f5af1f 100644 --- a/pkg/plugin/clientmgmt/process/client_builder.go +++ b/pkg/plugin/clientmgmt/process/client_builder.go @@ -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. diff --git a/pkg/plugin/clientmgmt/process/client_builder_test.go b/pkg/plugin/clientmgmt/process/client_builder_test.go index cff06d93fb..b0e7fa7004 100644 --- a/pkg/plugin/clientmgmt/process/client_builder_test.go +++ b/pkg/plugin/clientmgmt/process/client_builder_test.go @@ -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...), diff --git a/pkg/plugin/framework/action_resolver.go b/pkg/plugin/framework/action_resolver.go index 4b79fea9cf..ac8a0b1d09 100644 --- a/pkg/plugin/framework/action_resolver.go +++ b/pkg/plugin/framework/action_resolver.go @@ -27,6 +27,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" "github.com/vmware-tanzu/velero/pkg/util/collections" @@ -280,3 +281,38 @@ func (recv DeleteItemActionResolver) ResolveActions(helper discovery.Helper, log } return resolved, nil } + +type ItemBlockResolvedAction struct { + ibav1.ItemBlockAction + resolvedAction +} + +type ItemBlockActionResolver struct { + actions []ibav1.ItemBlockAction +} + +func NewItemBlockActionResolver(actions []ibav1.ItemBlockAction) ItemBlockActionResolver { + return ItemBlockActionResolver{ + actions: actions, + } +} + +func (recv ItemBlockActionResolver) ResolveActions(helper discovery.Helper, log logrus.FieldLogger) ([]ItemBlockResolvedAction, error) { + var resolved []ItemBlockResolvedAction + for _, action := range recv.actions { + resources, namespaces, selector, err := resolveAction(helper, action) + if err != nil { + return nil, err + } + res := ItemBlockResolvedAction{ + ItemBlockAction: action, + resolvedAction: resolvedAction{ + ResourceIncludesExcludes: resources, + NamespaceIncludesExcludes: namespaces, + Selector: selector, + }, + } + resolved = append(resolved, res) + } + return resolved, nil +} diff --git a/pkg/plugin/framework/common/plugin_kinds.go b/pkg/plugin/framework/common/plugin_kinds.go index 83cbcef310..b5c2c1c82b 100644 --- a/pkg/plugin/framework/common/plugin_kinds.go +++ b/pkg/plugin/framework/common/plugin_kinds.go @@ -47,6 +47,9 @@ const ( // PluginKindDeleteItemAction represents a delete item action plugin. PluginKindDeleteItemAction PluginKind = "DeleteItemAction" + // PluginKindItemBlockAction represents a v1 ItemBlock action plugin. + PluginKindItemBlockAction PluginKind = "ItemBlockAction" + // PluginKindPluginLister represents a plugin lister plugin. PluginKindPluginLister PluginKind = "PluginLister" ) @@ -70,5 +73,6 @@ func AllPluginKinds() map[string]PluginKind { allPluginKinds[PluginKindRestoreItemAction.String()] = PluginKindRestoreItemAction allPluginKinds[PluginKindRestoreItemActionV2.String()] = PluginKindRestoreItemActionV2 allPluginKinds[PluginKindDeleteItemAction.String()] = PluginKindDeleteItemAction + allPluginKinds[PluginKindItemBlockAction.String()] = PluginKindItemBlockAction return allPluginKinds } diff --git a/pkg/plugin/framework/itemblockaction/v1/item_block_action.go b/pkg/plugin/framework/itemblockaction/v1/item_block_action.go new file mode 100644 index 0000000000..dfe7a83b48 --- /dev/null +++ b/pkg/plugin/framework/itemblockaction/v1/item_block_action.go @@ -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 v1 + +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" + protoibav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated/itemblockaction/v1" +) + +// ItemBlockActionPlugin is an implementation of go-plugin's Plugin +// interface with support for gRPC for the backup/ItemAction +// interface. +type ItemBlockActionPlugin struct { + plugin.NetRPCUnsupportedPlugin + *common.PluginBase +} + +// GRPCClient returns a clientDispenser for ItemBlockAction gRPC clients. +func (p *ItemBlockActionPlugin) GRPCClient(_ context.Context, _ *plugin.GRPCBroker, clientConn *grpc.ClientConn) (interface{}, error) { + return common.NewClientDispenser(p.ClientLogger, clientConn, newItemBlockActionGRPCClient), nil +} + +// GRPCServer registers a ItemBlockAction gRPC server. +func (p *ItemBlockActionPlugin) GRPCServer(_ *plugin.GRPCBroker, server *grpc.Server) error { + protoibav1.RegisterItemBlockActionServer(server, &ItemBlockActionGRPCServer{mux: p.ServerMux}) + return nil +} diff --git a/pkg/plugin/framework/itemblockaction/v1/item_block_action_client.go b/pkg/plugin/framework/itemblockaction/v1/item_block_action_client.go new file mode 100644 index 0000000000..06287da33c --- /dev/null +++ b/pkg/plugin/framework/itemblockaction/v1/item_block_action_client.go @@ -0,0 +1,122 @@ +/* +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 ( + "encoding/json" + + "github.com/pkg/errors" + "golang.org/x/net/context" + "google.golang.org/grpc" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + + api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" + protoibav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated/itemblockaction/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" +) + +// NewItemBlockActionPlugin constructs a ItemBlockActionPlugin. +func NewItemBlockActionPlugin(options ...common.PluginOption) *ItemBlockActionPlugin { + return &ItemBlockActionPlugin{ + PluginBase: common.NewPluginBase(options...), + } +} + +// ItemBlockActionGRPCClient implements the backup/ItemAction interface and uses a +// gRPC client to make calls to the plugin server. +type ItemBlockActionGRPCClient struct { + *common.ClientBase + grpcClient protoibav1.ItemBlockActionClient +} + +func newItemBlockActionGRPCClient(base *common.ClientBase, clientConn *grpc.ClientConn) interface{} { + return &ItemBlockActionGRPCClient{ + ClientBase: base, + grpcClient: protoibav1.NewItemBlockActionClient(clientConn), + } +} + +func (c *ItemBlockActionGRPCClient) AppliesTo() (velero.ResourceSelector, error) { + req := &protoibav1.ItemBlockActionAppliesToRequest{ + Plugin: c.Plugin, + } + + res, err := c.grpcClient.AppliesTo(context.Background(), req) + 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 *ItemBlockActionGRPCClient) GetRelatedItems(item runtime.Unstructured, backup *api.Backup) ([]velero.ResourceIdentifier, error) { + itemJSON, err := json.Marshal(item.UnstructuredContent()) + if err != nil { + return nil, errors.WithStack(err) + } + + backupJSON, err := json.Marshal(backup) + if err != nil { + return nil, errors.WithStack(err) + } + + req := &protoibav1.ItemBlockActionGetRelatedItemsRequest{ + Plugin: c.Plugin, + Item: itemJSON, + Backup: backupJSON, + } + + res, err := c.grpcClient.GetRelatedItems(context.Background(), req) + if err != nil { + return nil, common.FromGRPCError(err) + } + + var relatedItems []velero.ResourceIdentifier + + for _, itm := range res.RelatedItems { + newItem := velero.ResourceIdentifier{ + GroupResource: schema.GroupResource{ + Group: itm.Group, + Resource: itm.Resource, + }, + Namespace: itm.Namespace, + Name: itm.Name, + } + + relatedItems = append(relatedItems, newItem) + } + + return relatedItems, nil +} + +// This shouldn't be called on the GRPC client since the RestartableItemBlockAction won't delegate +// this method +func (c *ItemBlockActionGRPCClient) Name() string { + return "" +} diff --git a/pkg/plugin/framework/itemblockaction/v1/item_block_action_server.go b/pkg/plugin/framework/itemblockaction/v1/item_block_action_server.go new file mode 100644 index 0000000000..17ece020da --- /dev/null +++ b/pkg/plugin/framework/itemblockaction/v1/item_block_action_server.go @@ -0,0 +1,134 @@ +/* +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 ( + "encoding/json" + + "github.com/pkg/errors" + "golang.org/x/net/context" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + + 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" + protoibav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated/itemblockaction/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" + ibav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/itemblockaction/v1" +) + +// ItemBlockActionGRPCServer implements the proto-generated ItemBlockAction interface, and accepts +// gRPC calls and forwards them to an implementation of the pluggable interface. +type ItemBlockActionGRPCServer struct { + mux *common.ServerMux +} + +func (s *ItemBlockActionGRPCServer) getImpl(name string) (ibav1.ItemBlockAction, error) { + impl, err := s.mux.GetHandler(name) + if err != nil { + return nil, err + } + + itemAction, ok := impl.(ibav1.ItemBlockAction) + if !ok { + return nil, errors.Errorf("%T is not a backup item action", impl) + } + + return itemAction, nil +} + +func (s *ItemBlockActionGRPCServer) AppliesTo( + ctx context.Context, req *protoibav1.ItemBlockActionAppliesToRequest) ( + response *protoibav1.ItemBlockActionAppliesToResponse, 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 &protoibav1.ItemBlockActionAppliesToResponse{ + ResourceSelector: &proto.ResourceSelector{ + IncludedNamespaces: resourceSelector.IncludedNamespaces, + ExcludedNamespaces: resourceSelector.ExcludedNamespaces, + IncludedResources: resourceSelector.IncludedResources, + ExcludedResources: resourceSelector.ExcludedResources, + Selector: resourceSelector.LabelSelector, + }, + }, nil +} + +func (s *ItemBlockActionGRPCServer) GetRelatedItems( + ctx context.Context, req *protoibav1.ItemBlockActionGetRelatedItemsRequest) (response *protoibav1.ItemBlockActionGetRelatedItemsResponse, 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 + var backup api.Backup + + if err := json.Unmarshal(req.Item, &item); err != nil { + return nil, common.NewGRPCError(errors.WithStack(err)) + } + if err := json.Unmarshal(req.Backup, &backup); err != nil { + return nil, common.NewGRPCError(errors.WithStack(err)) + } + + relatedItems, err := impl.GetRelatedItems(&item, &backup) + if err != nil { + return nil, common.NewGRPCError(err) + } + + res := &protoibav1.ItemBlockActionGetRelatedItemsResponse{} + + for _, item := range relatedItems { + res.RelatedItems = append(res.RelatedItems, backupResourceIdentifierToProto(item)) + } + + return res, nil +} + +func backupResourceIdentifierToProto(id velero.ResourceIdentifier) *proto.ResourceIdentifier { + return &proto.ResourceIdentifier{ + Group: id.Group, + Resource: id.Resource, + Namespace: id.Namespace, + Name: id.Name, + } +} + +// This shouldn't be called on the GRPC server since the server won't ever receive this request, as +// the RestartableItemBlockAction in Velero won't delegate this to the server +func (s *ItemBlockActionGRPCServer) Name() string { + return "" +} diff --git a/pkg/plugin/framework/itemblockaction/v1/item_block_action_test.go b/pkg/plugin/framework/itemblockaction/v1/item_block_action_test.go new file mode 100644 index 0000000000..ecfa693ae2 --- /dev/null +++ b/pkg/plugin/framework/itemblockaction/v1/item_block_action_test.go @@ -0,0 +1,163 @@ +/* +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 ( + "encoding/json" + "testing" + + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + + v1 "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" + protoibav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated/itemblockaction/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" + mocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks/itemblockaction/v1" + velerotest "github.com/vmware-tanzu/velero/pkg/test" +) + +func TestItemBlockActionGRPCServerGetRelatedItems(t *testing.T) { + invalidItem := []byte("this is gibberish json") + validItem := []byte(` + { + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": { + "namespace": "myns", + "name": "myconfigmap" + }, + "data": { + "key": "value" + } + }`) + var validItemObject unstructured.Unstructured + err := json.Unmarshal(validItem, &validItemObject) + require.NoError(t, err) + + invalidBackup := []byte("this is gibberish json") + validBackup := []byte(` + { + "apiVersion": "velero.io/v1", + "kind": "Backup", + "metadata": { + "namespace": "myns", + "name": "mybackup" + }, + "spec": { + "includedNamespaces": ["*"], + "includedResources": ["*"], + "ttl": "60m" + } + }`) + var validBackupObject v1.Backup + err = json.Unmarshal(validBackup, &validBackupObject) + require.NoError(t, err) + + tests := []struct { + name string + backup []byte + item []byte + implRelatedItems []velero.ResourceIdentifier + implError error + expectError bool + skipMock bool + }{ + { + name: "error unmarshaling item", + item: invalidItem, + backup: validBackup, + expectError: true, + skipMock: true, + }, + { + name: "error unmarshaling backup", + item: validItem, + backup: invalidBackup, + expectError: true, + skipMock: true, + }, + { + name: "error running impl", + item: validItem, + backup: validBackup, + implError: errors.New("impl error"), + expectError: true, + }, + { + name: "no relatedItems", + item: validItem, + backup: validBackup, + }, + { + name: "some relatedItems", + item: validItem, + backup: validBackup, + implRelatedItems: []velero.ResourceIdentifier{ + { + GroupResource: schema.GroupResource{Group: "v1", Resource: "pods"}, + Namespace: "myns", + Name: "mypod", + }, + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + itemAction := &mocks.ItemBlockAction{} + defer itemAction.AssertExpectations(t) + + if !test.skipMock { + itemAction.On("GetRelatedItems", &validItemObject, &validBackupObject).Return(test.implRelatedItems, test.implError) + } + + s := &ItemBlockActionGRPCServer{mux: &common.ServerMux{ + ServerLog: velerotest.NewLogger(), + Handlers: map[string]interface{}{ + "xyz": itemAction, + }, + }} + + req := &protoibav1.ItemBlockActionGetRelatedItemsRequest{ + Plugin: "xyz", + Item: test.item, + Backup: test.backup, + } + + resp, err := s.GetRelatedItems(context.Background(), req) + + // Verify error + assert.Equal(t, test.expectError, err != nil) + if err != nil { + return + } + require.NotNil(t, resp) + + // Verify related items + var expectedRelatedItems []*proto.ResourceIdentifier + for _, item := range test.implRelatedItems { + expectedRelatedItems = append(expectedRelatedItems, backupResourceIdentifierToProto(item)) + } + assert.Equal(t, expectedRelatedItems, resp.RelatedItems) + }) + } +} diff --git a/pkg/plugin/framework/server.go b/pkg/plugin/framework/server.go index d1d87aecb3..0e71c3b247 100644 --- a/pkg/plugin/framework/server.go +++ b/pkg/plugin/framework/server.go @@ -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" + 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/util/logging" ) @@ -90,6 +91,13 @@ type Server interface { // RegisterDeleteItemActions registers multiple Delete item actions. RegisterDeleteItemActions(map[string]common.HandlerInitializer) Server + // RegisterItemBlockAction registers a ItemBlock action. Accepted format + // for the plugin name is /. + RegisterItemBlockAction(pluginName string, initializer common.HandlerInitializer) Server + + // RegisterItemBlockActions registers multiple ItemBlock actions. + RegisterItemBlockActions(map[string]common.HandlerInitializer) Server + // Server runs the plugin server. Serve() } @@ -106,6 +114,7 @@ type server struct { restoreItemAction *RestoreItemActionPlugin restoreItemActionV2 *riav2.RestoreItemActionPlugin deleteItemAction *DeleteItemActionPlugin + itemBlockAction *ibav1.ItemBlockActionPlugin } // NewServer returns a new Server @@ -122,6 +131,7 @@ func NewServer() Server { restoreItemAction: NewRestoreItemActionPlugin(common.ServerLogger(log)), restoreItemActionV2: riav2.NewRestoreItemActionPlugin(common.ServerLogger(log)), deleteItemAction: NewDeleteItemActionPlugin(common.ServerLogger(log)), + itemBlockAction: ibav1.NewItemBlockActionPlugin(common.ServerLogger(log)), } } @@ -217,6 +227,18 @@ func (s *server) RegisterDeleteItemActions(m map[string]common.HandlerInitialize return s } +func (s *server) RegisterItemBlockAction(name string, initializer common.HandlerInitializer) Server { + s.itemBlockAction.Register(name, initializer) + return s +} + +func (s *server) RegisterItemBlockActions(m map[string]common.HandlerInitializer) Server { + for name := range m { + s.RegisterItemBlockAction(name, m[name]) + } + return s +} + // getNames returns a list of PluginIdentifiers registered with plugin. func getNames(command string, kind common.PluginKind, plugin Interface) []PluginIdentifier { var pluginIdentifiers []PluginIdentifier @@ -251,6 +273,7 @@ func (s *server) Serve() { 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.PluginKindItemBlockAction, s.itemBlockAction)...) pluginLister := NewPluginLister(pluginIdentifiers...) @@ -265,6 +288,7 @@ func (s *server) Serve() { string(common.PluginKindRestoreItemAction): s.restoreItemAction, string(common.PluginKindRestoreItemActionV2): s.restoreItemActionV2, string(common.PluginKindDeleteItemAction): s.deleteItemAction, + string(common.PluginKindItemBlockAction): s.itemBlockAction, }, GRPCServer: plugin.DefaultGRPCServer, }) diff --git a/pkg/plugin/generated/itemblockaction/v1/ItemBlockAction.pb.go b/pkg/plugin/generated/itemblockaction/v1/ItemBlockAction.pb.go new file mode 100644 index 0000000000..cec604477b --- /dev/null +++ b/pkg/plugin/generated/itemblockaction/v1/ItemBlockAction.pb.go @@ -0,0 +1,388 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.33.0 +// protoc v4.25.2 +// source: itemblockaction/v1/ItemBlockAction.proto + +package v1 + +import ( + generated "github.com/vmware-tanzu/velero/pkg/plugin/generated" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ItemBlockActionAppliesToRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` +} + +func (x *ItemBlockActionAppliesToRequest) Reset() { + *x = ItemBlockActionAppliesToRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ItemBlockActionAppliesToRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ItemBlockActionAppliesToRequest) ProtoMessage() {} + +func (x *ItemBlockActionAppliesToRequest) ProtoReflect() protoreflect.Message { + mi := &file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ItemBlockActionAppliesToRequest.ProtoReflect.Descriptor instead. +func (*ItemBlockActionAppliesToRequest) Descriptor() ([]byte, []int) { + return file_itemblockaction_v1_ItemBlockAction_proto_rawDescGZIP(), []int{0} +} + +func (x *ItemBlockActionAppliesToRequest) GetPlugin() string { + if x != nil { + return x.Plugin + } + return "" +} + +type ItemBlockActionAppliesToResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ResourceSelector *generated.ResourceSelector `protobuf:"bytes,1,opt,name=ResourceSelector,proto3" json:"ResourceSelector,omitempty"` +} + +func (x *ItemBlockActionAppliesToResponse) Reset() { + *x = ItemBlockActionAppliesToResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ItemBlockActionAppliesToResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ItemBlockActionAppliesToResponse) ProtoMessage() {} + +func (x *ItemBlockActionAppliesToResponse) ProtoReflect() protoreflect.Message { + mi := &file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ItemBlockActionAppliesToResponse.ProtoReflect.Descriptor instead. +func (*ItemBlockActionAppliesToResponse) Descriptor() ([]byte, []int) { + return file_itemblockaction_v1_ItemBlockAction_proto_rawDescGZIP(), []int{1} +} + +func (x *ItemBlockActionAppliesToResponse) GetResourceSelector() *generated.ResourceSelector { + if x != nil { + return x.ResourceSelector + } + return nil +} + +type ItemBlockActionGetRelatedItemsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + Item []byte `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` + Backup []byte `protobuf:"bytes,3,opt,name=backup,proto3" json:"backup,omitempty"` +} + +func (x *ItemBlockActionGetRelatedItemsRequest) Reset() { + *x = ItemBlockActionGetRelatedItemsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ItemBlockActionGetRelatedItemsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ItemBlockActionGetRelatedItemsRequest) ProtoMessage() {} + +func (x *ItemBlockActionGetRelatedItemsRequest) ProtoReflect() protoreflect.Message { + mi := &file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ItemBlockActionGetRelatedItemsRequest.ProtoReflect.Descriptor instead. +func (*ItemBlockActionGetRelatedItemsRequest) Descriptor() ([]byte, []int) { + return file_itemblockaction_v1_ItemBlockAction_proto_rawDescGZIP(), []int{2} +} + +func (x *ItemBlockActionGetRelatedItemsRequest) GetPlugin() string { + if x != nil { + return x.Plugin + } + return "" +} + +func (x *ItemBlockActionGetRelatedItemsRequest) GetItem() []byte { + if x != nil { + return x.Item + } + return nil +} + +func (x *ItemBlockActionGetRelatedItemsRequest) GetBackup() []byte { + if x != nil { + return x.Backup + } + return nil +} + +type ItemBlockActionGetRelatedItemsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RelatedItems []*generated.ResourceIdentifier `protobuf:"bytes,1,rep,name=relatedItems,proto3" json:"relatedItems,omitempty"` +} + +func (x *ItemBlockActionGetRelatedItemsResponse) Reset() { + *x = ItemBlockActionGetRelatedItemsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ItemBlockActionGetRelatedItemsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ItemBlockActionGetRelatedItemsResponse) ProtoMessage() {} + +func (x *ItemBlockActionGetRelatedItemsResponse) ProtoReflect() protoreflect.Message { + mi := &file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ItemBlockActionGetRelatedItemsResponse.ProtoReflect.Descriptor instead. +func (*ItemBlockActionGetRelatedItemsResponse) Descriptor() ([]byte, []int) { + return file_itemblockaction_v1_ItemBlockAction_proto_rawDescGZIP(), []int{3} +} + +func (x *ItemBlockActionGetRelatedItemsResponse) GetRelatedItems() []*generated.ResourceIdentifier { + if x != nil { + return x.RelatedItems + } + return nil +} + +var File_itemblockaction_v1_ItemBlockAction_proto protoreflect.FileDescriptor + +var file_itemblockaction_v1_ItemBlockAction_proto_rawDesc = []byte{ + 0x0a, 0x28, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x2f, 0x76, 0x31, 0x2f, 0x49, 0x74, 0x65, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x76, 0x31, 0x1a, 0x0c, + 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x39, 0x0a, 0x1f, + 0x49, 0x74, 0x65, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, + 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x22, 0x6b, 0x0a, 0x20, 0x49, 0x74, 0x65, 0x6d, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, + 0x73, 0x54, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x10, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x52, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x22, 0x6b, 0x0a, 0x25, 0x49, 0x74, 0x65, 0x6d, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, + 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, + 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x22, 0x6b, 0x0a, 0x26, 0x49, 0x74, 0x65, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x49, 0x74, + 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x72, + 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x32, 0xd3, + 0x01, 0x0a, 0x0f, 0x49, 0x74, 0x65, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x56, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x12, + 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, + 0x54, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x68, 0x0a, 0x0f, 0x47, 0x65, + 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x29, 0x2e, + 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x49, 0x74, 0x65, 0x6d, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, + 0x65, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x65, 0x74, + 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x76, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x74, 0x61, 0x6e, 0x7a, 0x75, 0x2f, + 0x76, 0x65, 0x6c, 0x65, 0x72, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2f, 0x69, 0x74, 0x65, 0x6d, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_itemblockaction_v1_ItemBlockAction_proto_rawDescOnce sync.Once + file_itemblockaction_v1_ItemBlockAction_proto_rawDescData = file_itemblockaction_v1_ItemBlockAction_proto_rawDesc +) + +func file_itemblockaction_v1_ItemBlockAction_proto_rawDescGZIP() []byte { + file_itemblockaction_v1_ItemBlockAction_proto_rawDescOnce.Do(func() { + file_itemblockaction_v1_ItemBlockAction_proto_rawDescData = protoimpl.X.CompressGZIP(file_itemblockaction_v1_ItemBlockAction_proto_rawDescData) + }) + return file_itemblockaction_v1_ItemBlockAction_proto_rawDescData +} + +var file_itemblockaction_v1_ItemBlockAction_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_itemblockaction_v1_ItemBlockAction_proto_goTypes = []interface{}{ + (*ItemBlockActionAppliesToRequest)(nil), // 0: v1.ItemBlockActionAppliesToRequest + (*ItemBlockActionAppliesToResponse)(nil), // 1: v1.ItemBlockActionAppliesToResponse + (*ItemBlockActionGetRelatedItemsRequest)(nil), // 2: v1.ItemBlockActionGetRelatedItemsRequest + (*ItemBlockActionGetRelatedItemsResponse)(nil), // 3: v1.ItemBlockActionGetRelatedItemsResponse + (*generated.ResourceSelector)(nil), // 4: generated.ResourceSelector + (*generated.ResourceIdentifier)(nil), // 5: generated.ResourceIdentifier +} +var file_itemblockaction_v1_ItemBlockAction_proto_depIdxs = []int32{ + 4, // 0: v1.ItemBlockActionAppliesToResponse.ResourceSelector:type_name -> generated.ResourceSelector + 5, // 1: v1.ItemBlockActionGetRelatedItemsResponse.relatedItems:type_name -> generated.ResourceIdentifier + 0, // 2: v1.ItemBlockAction.AppliesTo:input_type -> v1.ItemBlockActionAppliesToRequest + 2, // 3: v1.ItemBlockAction.GetRelatedItems:input_type -> v1.ItemBlockActionGetRelatedItemsRequest + 1, // 4: v1.ItemBlockAction.AppliesTo:output_type -> v1.ItemBlockActionAppliesToResponse + 3, // 5: v1.ItemBlockAction.GetRelatedItems:output_type -> v1.ItemBlockActionGetRelatedItemsResponse + 4, // [4:6] is the sub-list for method output_type + 2, // [2:4] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_itemblockaction_v1_ItemBlockAction_proto_init() } +func file_itemblockaction_v1_ItemBlockAction_proto_init() { + if File_itemblockaction_v1_ItemBlockAction_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ItemBlockActionAppliesToRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ItemBlockActionAppliesToResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ItemBlockActionGetRelatedItemsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ItemBlockActionGetRelatedItemsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_itemblockaction_v1_ItemBlockAction_proto_rawDesc, + NumEnums: 0, + NumMessages: 4, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_itemblockaction_v1_ItemBlockAction_proto_goTypes, + DependencyIndexes: file_itemblockaction_v1_ItemBlockAction_proto_depIdxs, + MessageInfos: file_itemblockaction_v1_ItemBlockAction_proto_msgTypes, + }.Build() + File_itemblockaction_v1_ItemBlockAction_proto = out.File + file_itemblockaction_v1_ItemBlockAction_proto_rawDesc = nil + file_itemblockaction_v1_ItemBlockAction_proto_goTypes = nil + file_itemblockaction_v1_ItemBlockAction_proto_depIdxs = nil +} diff --git a/pkg/plugin/generated/itemblockaction/v1/ItemBlockAction_grpc.pb.go b/pkg/plugin/generated/itemblockaction/v1/ItemBlockAction_grpc.pb.go new file mode 100644 index 0000000000..a83818d485 --- /dev/null +++ b/pkg/plugin/generated/itemblockaction/v1/ItemBlockAction_grpc.pb.go @@ -0,0 +1,144 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v4.25.2 +// source: itemblockaction/v1/ItemBlockAction.proto + +package v1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + ItemBlockAction_AppliesTo_FullMethodName = "/v1.ItemBlockAction/AppliesTo" + ItemBlockAction_GetRelatedItems_FullMethodName = "/v1.ItemBlockAction/GetRelatedItems" +) + +// ItemBlockActionClient is the client API for ItemBlockAction service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ItemBlockActionClient interface { + AppliesTo(ctx context.Context, in *ItemBlockActionAppliesToRequest, opts ...grpc.CallOption) (*ItemBlockActionAppliesToResponse, error) + GetRelatedItems(ctx context.Context, in *ItemBlockActionGetRelatedItemsRequest, opts ...grpc.CallOption) (*ItemBlockActionGetRelatedItemsResponse, error) +} + +type itemBlockActionClient struct { + cc grpc.ClientConnInterface +} + +func NewItemBlockActionClient(cc grpc.ClientConnInterface) ItemBlockActionClient { + return &itemBlockActionClient{cc} +} + +func (c *itemBlockActionClient) AppliesTo(ctx context.Context, in *ItemBlockActionAppliesToRequest, opts ...grpc.CallOption) (*ItemBlockActionAppliesToResponse, error) { + out := new(ItemBlockActionAppliesToResponse) + err := c.cc.Invoke(ctx, ItemBlockAction_AppliesTo_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *itemBlockActionClient) GetRelatedItems(ctx context.Context, in *ItemBlockActionGetRelatedItemsRequest, opts ...grpc.CallOption) (*ItemBlockActionGetRelatedItemsResponse, error) { + out := new(ItemBlockActionGetRelatedItemsResponse) + err := c.cc.Invoke(ctx, ItemBlockAction_GetRelatedItems_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ItemBlockActionServer is the server API for ItemBlockAction service. +// All implementations should embed UnimplementedItemBlockActionServer +// for forward compatibility +type ItemBlockActionServer interface { + AppliesTo(context.Context, *ItemBlockActionAppliesToRequest) (*ItemBlockActionAppliesToResponse, error) + GetRelatedItems(context.Context, *ItemBlockActionGetRelatedItemsRequest) (*ItemBlockActionGetRelatedItemsResponse, error) +} + +// UnimplementedItemBlockActionServer should be embedded to have forward compatible implementations. +type UnimplementedItemBlockActionServer struct { +} + +func (UnimplementedItemBlockActionServer) AppliesTo(context.Context, *ItemBlockActionAppliesToRequest) (*ItemBlockActionAppliesToResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AppliesTo not implemented") +} +func (UnimplementedItemBlockActionServer) GetRelatedItems(context.Context, *ItemBlockActionGetRelatedItemsRequest) (*ItemBlockActionGetRelatedItemsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetRelatedItems not implemented") +} + +// UnsafeItemBlockActionServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ItemBlockActionServer will +// result in compilation errors. +type UnsafeItemBlockActionServer interface { + mustEmbedUnimplementedItemBlockActionServer() +} + +func RegisterItemBlockActionServer(s grpc.ServiceRegistrar, srv ItemBlockActionServer) { + s.RegisterService(&ItemBlockAction_ServiceDesc, srv) +} + +func _ItemBlockAction_AppliesTo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ItemBlockActionAppliesToRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ItemBlockActionServer).AppliesTo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ItemBlockAction_AppliesTo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ItemBlockActionServer).AppliesTo(ctx, req.(*ItemBlockActionAppliesToRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ItemBlockAction_GetRelatedItems_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ItemBlockActionGetRelatedItemsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ItemBlockActionServer).GetRelatedItems(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ItemBlockAction_GetRelatedItems_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ItemBlockActionServer).GetRelatedItems(ctx, req.(*ItemBlockActionGetRelatedItemsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// ItemBlockAction_ServiceDesc is the grpc.ServiceDesc for ItemBlockAction service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var ItemBlockAction_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "v1.ItemBlockAction", + HandlerType: (*ItemBlockActionServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "AppliesTo", + Handler: _ItemBlockAction_AppliesTo_Handler, + }, + { + MethodName: "GetRelatedItems", + Handler: _ItemBlockAction_GetRelatedItems_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "itemblockaction/v1/ItemBlockAction.proto", +} diff --git a/pkg/plugin/mocks/manager.go b/pkg/plugin/mocks/manager.go index 8cb3274742..c12510fddd 100644 --- a/pkg/plugin/mocks/manager.go +++ b/pkg/plugin/mocks/manager.go @@ -1,9 +1,11 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks import ( mock "github.com/stretchr/testify/mock" + itemblockactionv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/itemblockaction/v1" + restoreitemactionv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" restoreitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2" @@ -31,7 +33,15 @@ func (_m *Manager) CleanupClients() { func (_m *Manager) GetBackupItemAction(name string) (v1.BackupItemAction, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for GetBackupItemAction") + } + var r0 v1.BackupItemAction + var r1 error + if rf, ok := ret.Get(0).(func(string) (v1.BackupItemAction, error)); ok { + return rf(name) + } if rf, ok := ret.Get(0).(func(string) v1.BackupItemAction); ok { r0 = rf(name) } else { @@ -40,7 +50,6 @@ func (_m *Manager) GetBackupItemAction(name string) (v1.BackupItemAction, error) } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(name) } else { @@ -54,7 +63,15 @@ func (_m *Manager) GetBackupItemAction(name string) (v1.BackupItemAction, error) func (_m *Manager) GetBackupItemActionV2(name string) (v2.BackupItemAction, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for GetBackupItemActionV2") + } + var r0 v2.BackupItemAction + var r1 error + if rf, ok := ret.Get(0).(func(string) (v2.BackupItemAction, error)); ok { + return rf(name) + } if rf, ok := ret.Get(0).(func(string) v2.BackupItemAction); ok { r0 = rf(name) } else { @@ -63,7 +80,6 @@ func (_m *Manager) GetBackupItemActionV2(name string) (v2.BackupItemAction, erro } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(name) } else { @@ -77,7 +93,15 @@ func (_m *Manager) GetBackupItemActionV2(name string) (v2.BackupItemAction, erro func (_m *Manager) GetBackupItemActions() ([]v1.BackupItemAction, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetBackupItemActions") + } + var r0 []v1.BackupItemAction + var r1 error + if rf, ok := ret.Get(0).(func() ([]v1.BackupItemAction, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() []v1.BackupItemAction); ok { r0 = rf() } else { @@ -86,7 +110,6 @@ func (_m *Manager) GetBackupItemActions() ([]v1.BackupItemAction, error) { } } - var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -100,7 +123,15 @@ func (_m *Manager) GetBackupItemActions() ([]v1.BackupItemAction, error) { func (_m *Manager) GetBackupItemActionsV2() ([]v2.BackupItemAction, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetBackupItemActionsV2") + } + var r0 []v2.BackupItemAction + var r1 error + if rf, ok := ret.Get(0).(func() ([]v2.BackupItemAction, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() []v2.BackupItemAction); ok { r0 = rf() } else { @@ -109,7 +140,6 @@ func (_m *Manager) GetBackupItemActionsV2() ([]v2.BackupItemAction, error) { } } - var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -123,7 +153,15 @@ func (_m *Manager) GetBackupItemActionsV2() ([]v2.BackupItemAction, error) { func (_m *Manager) GetDeleteItemAction(name string) (velero.DeleteItemAction, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for GetDeleteItemAction") + } + var r0 velero.DeleteItemAction + var r1 error + if rf, ok := ret.Get(0).(func(string) (velero.DeleteItemAction, error)); ok { + return rf(name) + } if rf, ok := ret.Get(0).(func(string) velero.DeleteItemAction); ok { r0 = rf(name) } else { @@ -132,7 +170,6 @@ func (_m *Manager) GetDeleteItemAction(name string) (velero.DeleteItemAction, er } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(name) } else { @@ -146,7 +183,15 @@ func (_m *Manager) GetDeleteItemAction(name string) (velero.DeleteItemAction, er func (_m *Manager) GetDeleteItemActions() ([]velero.DeleteItemAction, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetDeleteItemActions") + } + var r0 []velero.DeleteItemAction + var r1 error + if rf, ok := ret.Get(0).(func() ([]velero.DeleteItemAction, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() []velero.DeleteItemAction); ok { r0 = rf() } else { @@ -155,7 +200,66 @@ func (_m *Manager) GetDeleteItemActions() ([]velero.DeleteItemAction, error) { } } + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetItemBlockAction provides a mock function with given fields: name +func (_m *Manager) GetItemBlockAction(name string) (itemblockactionv1.ItemBlockAction, error) { + ret := _m.Called(name) + + if len(ret) == 0 { + panic("no return value specified for GetItemBlockAction") + } + + var r0 itemblockactionv1.ItemBlockAction var r1 error + if rf, ok := ret.Get(0).(func(string) (itemblockactionv1.ItemBlockAction, error)); ok { + return rf(name) + } + if rf, ok := ret.Get(0).(func(string) itemblockactionv1.ItemBlockAction); ok { + r0 = rf(name) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(itemblockactionv1.ItemBlockAction) + } + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(name) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetItemBlockActions provides a mock function with given fields: +func (_m *Manager) GetItemBlockActions() ([]itemblockactionv1.ItemBlockAction, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetItemBlockActions") + } + + var r0 []itemblockactionv1.ItemBlockAction + var r1 error + if rf, ok := ret.Get(0).(func() ([]itemblockactionv1.ItemBlockAction, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []itemblockactionv1.ItemBlockAction); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]itemblockactionv1.ItemBlockAction) + } + } + if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -169,7 +273,15 @@ func (_m *Manager) GetDeleteItemActions() ([]velero.DeleteItemAction, error) { func (_m *Manager) GetObjectStore(name string) (velero.ObjectStore, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for GetObjectStore") + } + var r0 velero.ObjectStore + var r1 error + if rf, ok := ret.Get(0).(func(string) (velero.ObjectStore, error)); ok { + return rf(name) + } if rf, ok := ret.Get(0).(func(string) velero.ObjectStore); ok { r0 = rf(name) } else { @@ -178,7 +290,6 @@ func (_m *Manager) GetObjectStore(name string) (velero.ObjectStore, error) { } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(name) } else { @@ -192,7 +303,15 @@ func (_m *Manager) GetObjectStore(name string) (velero.ObjectStore, error) { func (_m *Manager) GetRestoreItemAction(name string) (restoreitemactionv1.RestoreItemAction, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for GetRestoreItemAction") + } + var r0 restoreitemactionv1.RestoreItemAction + var r1 error + if rf, ok := ret.Get(0).(func(string) (restoreitemactionv1.RestoreItemAction, error)); ok { + return rf(name) + } if rf, ok := ret.Get(0).(func(string) restoreitemactionv1.RestoreItemAction); ok { r0 = rf(name) } else { @@ -201,7 +320,6 @@ func (_m *Manager) GetRestoreItemAction(name string) (restoreitemactionv1.Restor } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(name) } else { @@ -215,7 +333,15 @@ func (_m *Manager) GetRestoreItemAction(name string) (restoreitemactionv1.Restor func (_m *Manager) GetRestoreItemActionV2(name string) (restoreitemactionv2.RestoreItemAction, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for GetRestoreItemActionV2") + } + var r0 restoreitemactionv2.RestoreItemAction + var r1 error + if rf, ok := ret.Get(0).(func(string) (restoreitemactionv2.RestoreItemAction, error)); ok { + return rf(name) + } if rf, ok := ret.Get(0).(func(string) restoreitemactionv2.RestoreItemAction); ok { r0 = rf(name) } else { @@ -224,7 +350,6 @@ func (_m *Manager) GetRestoreItemActionV2(name string) (restoreitemactionv2.Rest } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(name) } else { @@ -238,7 +363,15 @@ func (_m *Manager) GetRestoreItemActionV2(name string) (restoreitemactionv2.Rest func (_m *Manager) GetRestoreItemActions() ([]restoreitemactionv1.RestoreItemAction, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetRestoreItemActions") + } + var r0 []restoreitemactionv1.RestoreItemAction + var r1 error + if rf, ok := ret.Get(0).(func() ([]restoreitemactionv1.RestoreItemAction, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() []restoreitemactionv1.RestoreItemAction); ok { r0 = rf() } else { @@ -247,7 +380,6 @@ func (_m *Manager) GetRestoreItemActions() ([]restoreitemactionv1.RestoreItemAct } } - var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -261,7 +393,15 @@ func (_m *Manager) GetRestoreItemActions() ([]restoreitemactionv1.RestoreItemAct func (_m *Manager) GetRestoreItemActionsV2() ([]restoreitemactionv2.RestoreItemAction, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetRestoreItemActionsV2") + } + var r0 []restoreitemactionv2.RestoreItemAction + var r1 error + if rf, ok := ret.Get(0).(func() ([]restoreitemactionv2.RestoreItemAction, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() []restoreitemactionv2.RestoreItemAction); ok { r0 = rf() } else { @@ -270,7 +410,6 @@ func (_m *Manager) GetRestoreItemActionsV2() ([]restoreitemactionv2.RestoreItemA } } - var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -284,7 +423,15 @@ func (_m *Manager) GetRestoreItemActionsV2() ([]restoreitemactionv2.RestoreItemA func (_m *Manager) GetVolumeSnapshotter(name string) (volumesnapshotterv1.VolumeSnapshotter, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for GetVolumeSnapshotter") + } + var r0 volumesnapshotterv1.VolumeSnapshotter + var r1 error + if rf, ok := ret.Get(0).(func(string) (volumesnapshotterv1.VolumeSnapshotter, error)); ok { + return rf(name) + } if rf, ok := ret.Get(0).(func(string) volumesnapshotterv1.VolumeSnapshotter); ok { r0 = rf(name) } else { @@ -293,7 +440,6 @@ func (_m *Manager) GetVolumeSnapshotter(name string) (volumesnapshotterv1.Volume } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(name) } else { @@ -302,3 +448,17 @@ func (_m *Manager) GetVolumeSnapshotter(name string) (volumesnapshotterv1.Volume return r0, r1 } + +// NewManager creates a new instance of Manager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewManager(t interface { + mock.TestingT + Cleanup(func()) +}) *Manager { + mock := &Manager{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/plugin/proto/itemblockaction/v1/ItemBlockAction.proto b/pkg/plugin/proto/itemblockaction/v1/ItemBlockAction.proto new file mode 100644 index 0000000000..5c0f86a3d8 --- /dev/null +++ b/pkg/plugin/proto/itemblockaction/v1/ItemBlockAction.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; +package v1; +option go_package = "github.com/vmware-tanzu/velero/pkg/plugin/generated/itemblockaction/v1"; + +import "Shared.proto"; + + +service ItemBlockAction { + rpc AppliesTo(ItemBlockActionAppliesToRequest) returns (ItemBlockActionAppliesToResponse); + rpc GetRelatedItems(ItemBlockActionGetRelatedItemsRequest) returns (ItemBlockActionGetRelatedItemsResponse); +} + +message ItemBlockActionAppliesToRequest { + string plugin = 1; +} + +message ItemBlockActionAppliesToResponse { + generated.ResourceSelector ResourceSelector = 1; +} + +message ItemBlockActionGetRelatedItemsRequest { + string plugin = 1; + bytes item = 2; + bytes backup = 3; +} + +message ItemBlockActionGetRelatedItemsResponse { + repeated generated.ResourceIdentifier relatedItems = 1; +} diff --git a/pkg/plugin/velero/itemblockaction/v1/item_block_action.go b/pkg/plugin/velero/itemblockaction/v1/item_block_action.go new file mode 100644 index 0000000000..6d01337008 --- /dev/null +++ b/pkg/plugin/velero/itemblockaction/v1/item_block_action.go @@ -0,0 +1,46 @@ +/* +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 ( + "k8s.io/apimachinery/pkg/runtime" + + api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" +) + +// ItemBlockAction is an action that returns a list of related items that must be backed up +// along with the current item (and not in a separate parallel backup thread). +type ItemBlockAction interface { + // Name returns the name of this IBA. Plugins which implement this interface must define Name, + // but its content is unimportant, as it won't actually be called via RPC. Velero's plugin infrastructure + // will implement this directly rather than delegating to the RPC plugin in order to return the name + // that the plugin was registered under. The plugins must implement the method to complete the interface. + Name() string + + // AppliesTo returns information about which resources this action should be invoked for. + // A ItemBlockAction's GetRelatedItems function will only be invoked on items that match the returned + // selector. A zero-valued ResourceSelector matches all resources. + AppliesTo() (velero.ResourceSelector, error) + + // GetRelatedItems allows the ItemBlockAction to identify related items which must be backed up + // along with the current item. In many cases, these will be the same items that a related + // BackupItemAction's Execute method will return as additionalItems, but there may be differences. + // For example, items that are newly-created in the BIA Execute and don't yet exist at backup + // start will *not* be returned here. + GetRelatedItems(item runtime.Unstructured, backup *api.Backup) ([]velero.ResourceIdentifier, error) +} diff --git a/pkg/plugin/velero/mocks/itemblockaction/v1/ItemBlockAction.go b/pkg/plugin/velero/mocks/itemblockaction/v1/ItemBlockAction.go new file mode 100644 index 0000000000..95a8d960a3 --- /dev/null +++ b/pkg/plugin/velero/mocks/itemblockaction/v1/ItemBlockAction.go @@ -0,0 +1,122 @@ +/* +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 v2.43.2. DO NOT EDIT. + +package v1 + +import ( + mock "github.com/stretchr/testify/mock" + runtime "k8s.io/apimachinery/pkg/runtime" + + velero "github.com/vmware-tanzu/velero/pkg/plugin/velero" + + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +// ItemBlockAction is an autogenerated mock type for the ItemBlockAction type +type ItemBlockAction struct { + mock.Mock +} + +// AppliesTo provides a mock function with given fields: +func (_m *ItemBlockAction) AppliesTo() (velero.ResourceSelector, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for AppliesTo") + } + + var r0 velero.ResourceSelector + var r1 error + if rf, ok := ret.Get(0).(func() (velero.ResourceSelector, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() velero.ResourceSelector); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(velero.ResourceSelector) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetRelatedItems provides a mock function with given fields: item, backup +func (_m *ItemBlockAction) GetRelatedItems(item runtime.Unstructured, backup *velerov1.Backup) ([]velero.ResourceIdentifier, error) { + ret := _m.Called(item, backup) + + if len(ret) == 0 { + panic("no return value specified for GetRelatedItems") + } + + var r0 []velero.ResourceIdentifier + var r1 error + if rf, ok := ret.Get(0).(func(runtime.Unstructured, *velerov1.Backup) ([]velero.ResourceIdentifier, error)); ok { + return rf(item, backup) + } + if rf, ok := ret.Get(0).(func(runtime.Unstructured, *velerov1.Backup) []velero.ResourceIdentifier); ok { + r0 = rf(item, backup) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]velero.ResourceIdentifier) + } + } + + if rf, ok := ret.Get(1).(func(runtime.Unstructured, *velerov1.Backup) error); ok { + r1 = rf(item, backup) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Name provides a mock function with given fields: +func (_m *ItemBlockAction) Name() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Name") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// NewItemBlockAction creates a new instance of ItemBlockAction. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewItemBlockAction(t interface { + mock.TestingT + Cleanup(func()) +}) *ItemBlockAction { + mock := &ItemBlockAction{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +}