mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-01-04 04:04:02 +00:00
Split plugin framework into its own package
Signed-off-by: Carlisia <carlisiac@vmware.com>
This commit is contained in:
@@ -26,7 +26,7 @@ import (
|
||||
"github.com/heptio/velero/pkg/cloudprovider/azure"
|
||||
"github.com/heptio/velero/pkg/cloudprovider/gcp"
|
||||
velerodiscovery "github.com/heptio/velero/pkg/discovery"
|
||||
veleroplugin "github.com/heptio/velero/pkg/plugin"
|
||||
veleroplugin "github.com/heptio/velero/pkg/plugin/framework"
|
||||
"github.com/heptio/velero/pkg/restore"
|
||||
)
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
"github.com/hashicorp/go-hclog"
|
||||
hcplugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/heptio/velero/pkg/plugin/framework"
|
||||
)
|
||||
|
||||
// clientBuilder builds go-plugin Clients.
|
||||
@@ -56,14 +58,14 @@ func newLogrusAdapter(pluginLogger logrus.FieldLogger, logLevel logrus.Level) *l
|
||||
|
||||
func (b *clientBuilder) clientConfig() *hcplugin.ClientConfig {
|
||||
return &hcplugin.ClientConfig{
|
||||
HandshakeConfig: Handshake,
|
||||
HandshakeConfig: framework.Handshake,
|
||||
AllowedProtocols: []hcplugin.Protocol{hcplugin.ProtocolGRPC},
|
||||
Plugins: map[string]hcplugin.Plugin{
|
||||
string(PluginKindBackupItemAction): NewBackupItemActionPlugin(clientLogger(b.clientLogger)),
|
||||
string(PluginKindBlockStore): NewBlockStorePlugin(clientLogger(b.clientLogger)),
|
||||
string(PluginKindObjectStore): NewObjectStorePlugin(clientLogger(b.clientLogger)),
|
||||
string(PluginKindPluginLister): &PluginListerPlugin{},
|
||||
string(PluginKindRestoreItemAction): NewRestoreItemActionPlugin(clientLogger(b.clientLogger)),
|
||||
string(framework.PluginKindBackupItemAction): framework.NewBackupItemActionPlugin(framework.ClientLogger(b.clientLogger)),
|
||||
string(framework.PluginKindBlockStore): framework.NewBlockStorePlugin(framework.ClientLogger(b.clientLogger)),
|
||||
string(framework.PluginKindObjectStore): framework.NewObjectStorePlugin(framework.ClientLogger(b.clientLogger)),
|
||||
string(framework.PluginKindPluginLister): &framework.PluginListerPlugin{},
|
||||
string(framework.PluginKindRestoreItemAction): framework.NewRestoreItemActionPlugin(framework.ClientLogger(b.clientLogger)),
|
||||
},
|
||||
Logger: b.pluginLogger,
|
||||
Cmd: exec.Command(b.commandName, b.commandArgs...),
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/heptio/velero/pkg/plugin/framework"
|
||||
"github.com/heptio/velero/pkg/util/test"
|
||||
)
|
||||
|
||||
@@ -47,14 +48,14 @@ func TestClientConfig(t *testing.T) {
|
||||
cb := newClientBuilder("velero", logger, logLevel)
|
||||
|
||||
expected := &hcplugin.ClientConfig{
|
||||
HandshakeConfig: Handshake,
|
||||
HandshakeConfig: framework.Handshake,
|
||||
AllowedProtocols: []hcplugin.Protocol{hcplugin.ProtocolGRPC},
|
||||
Plugins: map[string]hcplugin.Plugin{
|
||||
string(PluginKindBackupItemAction): NewBackupItemActionPlugin(clientLogger(logger)),
|
||||
string(PluginKindBlockStore): NewBlockStorePlugin(clientLogger(logger)),
|
||||
string(PluginKindObjectStore): NewObjectStorePlugin(clientLogger(logger)),
|
||||
string(PluginKindPluginLister): &PluginListerPlugin{},
|
||||
string(PluginKindRestoreItemAction): NewRestoreItemActionPlugin(clientLogger(logger)),
|
||||
string(framework.PluginKindBackupItemAction): framework.NewBackupItemActionPlugin(framework.ClientLogger(logger)),
|
||||
string(framework.PluginKindBlockStore): framework.NewBlockStorePlugin(framework.ClientLogger(logger)),
|
||||
string(framework.PluginKindObjectStore): framework.NewObjectStorePlugin(framework.ClientLogger(logger)),
|
||||
string(framework.PluginKindPluginLister): &framework.PluginListerPlugin{},
|
||||
string(framework.PluginKindRestoreItemAction): framework.NewRestoreItemActionPlugin(framework.ClientLogger(logger)),
|
||||
},
|
||||
Logger: cb.pluginLogger,
|
||||
Cmd: exec.Command(cb.commandName, cb.commandArgs...),
|
||||
|
||||
43
pkg/plugin/framework/backup_item_action.go
Normal file
43
pkg/plugin/framework/backup_item_action.go
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package framework
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
proto "github.com/heptio/velero/pkg/plugin/generated"
|
||||
)
|
||||
|
||||
// BackupItemActionPlugin is an implementation of go-plugin's Plugin
|
||||
// interface with support for gRPC for the backup/ItemAction
|
||||
// interface.
|
||||
type BackupItemActionPlugin struct {
|
||||
plugin.NetRPCUnsupportedPlugin
|
||||
*pluginBase
|
||||
}
|
||||
|
||||
// GRPCClient returns a clientDispenser for BackupItemAction gRPC clients.
|
||||
func (p *BackupItemActionPlugin) GRPCClient(c *grpc.ClientConn) (interface{}, error) {
|
||||
return newClientDispenser(p.clientLogger, c, newBackupItemActionGRPCClient), nil
|
||||
}
|
||||
|
||||
// GRPCServer registers a BackupItemAction gRPC server.
|
||||
func (p *BackupItemActionPlugin) GRPCServer(s *grpc.Server) error {
|
||||
proto.RegisterBackupItemActionServer(s, &BackupItemActionGRPCServer{mux: p.serverMux})
|
||||
return nil
|
||||
}
|
||||
112
pkg/plugin/framework/backup_item_action_client.go
Normal file
112
pkg/plugin/framework/backup_item_action_client.go
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
Copyright 2017, 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package framework
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
proto "github.com/heptio/velero/pkg/plugin/generated"
|
||||
"github.com/heptio/velero/pkg/plugin/velero"
|
||||
)
|
||||
|
||||
// NewBackupItemActionPlugin constructs a BackupItemActionPlugin.
|
||||
func NewBackupItemActionPlugin(options ...PluginOption) *BackupItemActionPlugin {
|
||||
return &BackupItemActionPlugin{
|
||||
pluginBase: newPluginBase(options...),
|
||||
}
|
||||
}
|
||||
|
||||
// BackupItemActionGRPCClient implements the backup/ItemAction interface and uses a
|
||||
// gRPC client to make calls to the plugin server.
|
||||
type BackupItemActionGRPCClient struct {
|
||||
*clientBase
|
||||
grpcClient proto.BackupItemActionClient
|
||||
}
|
||||
|
||||
func newBackupItemActionGRPCClient(base *clientBase, clientConn *grpc.ClientConn) interface{} {
|
||||
return &BackupItemActionGRPCClient{
|
||||
clientBase: base,
|
||||
grpcClient: proto.NewBackupItemActionClient(clientConn),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *BackupItemActionGRPCClient) AppliesTo() (velero.ResourceSelector, error) {
|
||||
res, err := c.grpcClient.AppliesTo(context.Background(), &proto.AppliesToRequest{Plugin: c.plugin})
|
||||
if err != nil {
|
||||
return velero.ResourceSelector{}, err
|
||||
}
|
||||
|
||||
return velero.ResourceSelector{
|
||||
IncludedNamespaces: res.IncludedNamespaces,
|
||||
ExcludedNamespaces: res.ExcludedNamespaces,
|
||||
IncludedResources: res.IncludedResources,
|
||||
ExcludedResources: res.ExcludedResources,
|
||||
LabelSelector: res.Selector,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *BackupItemActionGRPCClient) Execute(item runtime.Unstructured, backup *api.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) {
|
||||
itemJSON, err := json.Marshal(item.UnstructuredContent())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
backupJSON, err := json.Marshal(backup)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
req := &proto.ExecuteRequest{
|
||||
Plugin: c.plugin,
|
||||
Item: itemJSON,
|
||||
Backup: backupJSON,
|
||||
}
|
||||
|
||||
res, err := c.grpcClient.Execute(context.Background(), req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var updatedItem unstructured.Unstructured
|
||||
if err := json.Unmarshal(res.Item, &updatedItem); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var additionalItems []velero.ResourceIdentifier
|
||||
|
||||
for _, itm := range res.AdditionalItems {
|
||||
newItem := velero.ResourceIdentifier{
|
||||
GroupResource: schema.GroupResource{
|
||||
Group: itm.Group,
|
||||
Resource: itm.Resource,
|
||||
},
|
||||
Namespace: itm.Namespace,
|
||||
Name: itm.Name,
|
||||
}
|
||||
|
||||
additionalItems = append(additionalItems, newItem)
|
||||
}
|
||||
|
||||
return &updatedItem, additionalItems, nil
|
||||
}
|
||||
@@ -14,133 +14,21 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package plugin
|
||||
package framework
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
proto "github.com/heptio/velero/pkg/plugin/generated"
|
||||
"github.com/heptio/velero/pkg/plugin/velero"
|
||||
)
|
||||
|
||||
// BackupItemActionPlugin is an implementation of go-plugin's Plugin
|
||||
// interface with support for gRPC for the backup/ItemAction
|
||||
// interface.
|
||||
type BackupItemActionPlugin struct {
|
||||
plugin.NetRPCUnsupportedPlugin
|
||||
*pluginBase
|
||||
}
|
||||
|
||||
// NewBackupItemActionPlugin constructs a BackupItemActionPlugin.
|
||||
func NewBackupItemActionPlugin(options ...pluginOption) *BackupItemActionPlugin {
|
||||
return &BackupItemActionPlugin{
|
||||
pluginBase: newPluginBase(options...),
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// client code
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// GRPCClient returns a clientDispenser for BackupItemAction gRPC clients.
|
||||
func (p *BackupItemActionPlugin) GRPCClient(c *grpc.ClientConn) (interface{}, error) {
|
||||
return newClientDispenser(p.clientLogger, c, newBackupItemActionGRPCClient), nil
|
||||
}
|
||||
|
||||
// BackupItemActionGRPCClient implements the backup/ItemAction interface and uses a
|
||||
// gRPC client to make calls to the plugin server.
|
||||
type BackupItemActionGRPCClient struct {
|
||||
*clientBase
|
||||
grpcClient proto.BackupItemActionClient
|
||||
}
|
||||
|
||||
func newBackupItemActionGRPCClient(base *clientBase, clientConn *grpc.ClientConn) interface{} {
|
||||
return &BackupItemActionGRPCClient{
|
||||
clientBase: base,
|
||||
grpcClient: proto.NewBackupItemActionClient(clientConn),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *BackupItemActionGRPCClient) AppliesTo() (velero.ResourceSelector, error) {
|
||||
res, err := c.grpcClient.AppliesTo(context.Background(), &proto.AppliesToRequest{Plugin: c.plugin})
|
||||
if err != nil {
|
||||
return velero.ResourceSelector{}, err
|
||||
}
|
||||
|
||||
return velero.ResourceSelector{
|
||||
IncludedNamespaces: res.IncludedNamespaces,
|
||||
ExcludedNamespaces: res.ExcludedNamespaces,
|
||||
IncludedResources: res.IncludedResources,
|
||||
ExcludedResources: res.ExcludedResources,
|
||||
LabelSelector: res.Selector,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *BackupItemActionGRPCClient) Execute(item runtime.Unstructured, backup *api.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) {
|
||||
itemJSON, err := json.Marshal(item.UnstructuredContent())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
backupJSON, err := json.Marshal(backup)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
req := &proto.ExecuteRequest{
|
||||
Plugin: c.plugin,
|
||||
Item: itemJSON,
|
||||
Backup: backupJSON,
|
||||
}
|
||||
|
||||
res, err := c.grpcClient.Execute(context.Background(), req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var updatedItem unstructured.Unstructured
|
||||
if err := json.Unmarshal(res.Item, &updatedItem); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var additionalItems []velero.ResourceIdentifier
|
||||
|
||||
for _, itm := range res.AdditionalItems {
|
||||
newItem := velero.ResourceIdentifier{
|
||||
GroupResource: schema.GroupResource{
|
||||
Group: itm.Group,
|
||||
Resource: itm.Resource,
|
||||
},
|
||||
Namespace: itm.Namespace,
|
||||
Name: itm.Name,
|
||||
}
|
||||
|
||||
additionalItems = append(additionalItems, newItem)
|
||||
}
|
||||
|
||||
return &updatedItem, additionalItems, nil
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// server code
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// GRPCServer registers a BackupItemAction gRPC server.
|
||||
func (p *BackupItemActionPlugin) GRPCServer(s *grpc.Server) error {
|
||||
proto.RegisterBackupItemActionServer(s, &BackupItemActionGRPCServer{mux: p.serverMux})
|
||||
return nil
|
||||
}
|
||||
|
||||
// BackupItemActionGRPCServer implements the proto-generated BackupItemActionServer interface, and accepts
|
||||
// BackupItemActionGRPCServer implements the proto-generated BackupItemAction interface, and accepts
|
||||
// gRPC calls and forwards them to an implementation of the pluggable interface.
|
||||
type BackupItemActionGRPCServer struct {
|
||||
mux *serverMux
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package plugin
|
||||
package framework
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
43
pkg/plugin/framework/block_store.go
Normal file
43
pkg/plugin/framework/block_store.go
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package framework
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
proto "github.com/heptio/velero/pkg/plugin/generated"
|
||||
)
|
||||
|
||||
// BlockStorePlugin is an implementation of go-plugin's Plugin
|
||||
// interface with support for gRPC for the cloudprovider/BlockStore
|
||||
// interface.
|
||||
type BlockStorePlugin struct {
|
||||
plugin.NetRPCUnsupportedPlugin
|
||||
*pluginBase
|
||||
}
|
||||
|
||||
// GRPCClient returns a BlockStore gRPC client.
|
||||
func (p *BlockStorePlugin) GRPCClient(c *grpc.ClientConn) (interface{}, error) {
|
||||
return newClientDispenser(p.clientLogger, c, newBlockStoreGRPCClient), nil
|
||||
}
|
||||
|
||||
// GRPCServer registers a BlockStore gRPC server.
|
||||
func (p *BlockStorePlugin) GRPCServer(s *grpc.Server) error {
|
||||
proto.RegisterBlockStoreServer(s, &BlockStoreGRPCServer{mux: p.serverMux})
|
||||
return nil
|
||||
}
|
||||
168
pkg/plugin/framework/block_store_client.go
Normal file
168
pkg/plugin/framework/block_store_client.go
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
Copyright 2017, 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package framework
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
proto "github.com/heptio/velero/pkg/plugin/generated"
|
||||
)
|
||||
|
||||
// NewBlockStorePlugin constructs a BlockStorePlugin.
|
||||
func NewBlockStorePlugin(options ...PluginOption) *BlockStorePlugin {
|
||||
return &BlockStorePlugin{
|
||||
pluginBase: newPluginBase(options...),
|
||||
}
|
||||
}
|
||||
|
||||
// BlockStoreGRPCClient implements the cloudprovider.BlockStore interface and uses a
|
||||
// gRPC client to make calls to the plugin server.
|
||||
type BlockStoreGRPCClient struct {
|
||||
*clientBase
|
||||
grpcClient proto.BlockStoreClient
|
||||
}
|
||||
|
||||
func newBlockStoreGRPCClient(base *clientBase, clientConn *grpc.ClientConn) interface{} {
|
||||
return &BlockStoreGRPCClient{
|
||||
clientBase: base,
|
||||
grpcClient: proto.NewBlockStoreClient(clientConn),
|
||||
}
|
||||
}
|
||||
|
||||
// Init prepares the BlockStore for usage using the provided map of
|
||||
// configuration key-value pairs. It returns an error if the BlockStore
|
||||
// cannot be initialized from the provided config.
|
||||
func (c *BlockStoreGRPCClient) Init(config map[string]string) error {
|
||||
_, err := c.grpcClient.Init(context.Background(), &proto.InitRequest{Plugin: c.plugin, Config: config})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// CreateVolumeFromSnapshot creates a new block volume, initialized from the provided snapshot,
|
||||
// and with the specified type and IOPS (if using provisioned IOPS).
|
||||
func (c *BlockStoreGRPCClient) CreateVolumeFromSnapshot(snapshotID, volumeType, volumeAZ string, iops *int64) (string, error) {
|
||||
req := &proto.CreateVolumeRequest{
|
||||
Plugin: c.plugin,
|
||||
SnapshotID: snapshotID,
|
||||
VolumeType: volumeType,
|
||||
VolumeAZ: volumeAZ,
|
||||
}
|
||||
|
||||
if iops == nil {
|
||||
req.Iops = 0
|
||||
} else {
|
||||
req.Iops = *iops
|
||||
}
|
||||
|
||||
res, err := c.grpcClient.CreateVolumeFromSnapshot(context.Background(), req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return res.VolumeID, nil
|
||||
}
|
||||
|
||||
// GetVolumeInfo returns the type and IOPS (if using provisioned IOPS) for a specified block
|
||||
// volume.
|
||||
func (c *BlockStoreGRPCClient) GetVolumeInfo(volumeID, volumeAZ string) (string, *int64, error) {
|
||||
res, err := c.grpcClient.GetVolumeInfo(context.Background(), &proto.GetVolumeInfoRequest{Plugin: c.plugin, VolumeID: volumeID, VolumeAZ: volumeAZ})
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
var iops *int64
|
||||
if res.Iops != 0 {
|
||||
iops = &res.Iops
|
||||
}
|
||||
|
||||
return res.VolumeType, iops, nil
|
||||
}
|
||||
|
||||
// CreateSnapshot creates a snapshot of the specified block volume, and applies the provided
|
||||
// set of tags to the snapshot.
|
||||
func (c *BlockStoreGRPCClient) CreateSnapshot(volumeID, volumeAZ string, tags map[string]string) (string, error) {
|
||||
req := &proto.CreateSnapshotRequest{
|
||||
Plugin: c.plugin,
|
||||
VolumeID: volumeID,
|
||||
VolumeAZ: volumeAZ,
|
||||
Tags: tags,
|
||||
}
|
||||
|
||||
res, err := c.grpcClient.CreateSnapshot(context.Background(), req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return res.SnapshotID, nil
|
||||
}
|
||||
|
||||
// DeleteSnapshot deletes the specified volume snapshot.
|
||||
func (c *BlockStoreGRPCClient) DeleteSnapshot(snapshotID string) error {
|
||||
_, err := c.grpcClient.DeleteSnapshot(context.Background(), &proto.DeleteSnapshotRequest{Plugin: c.plugin, SnapshotID: snapshotID})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BlockStoreGRPCClient) GetVolumeID(pv runtime.Unstructured) (string, error) {
|
||||
encodedPV, err := json.Marshal(pv.UnstructuredContent())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
req := &proto.GetVolumeIDRequest{
|
||||
Plugin: c.plugin,
|
||||
PersistentVolume: encodedPV,
|
||||
}
|
||||
|
||||
resp, err := c.grpcClient.GetVolumeID(context.Background(), req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return resp.VolumeID, nil
|
||||
}
|
||||
|
||||
func (c *BlockStoreGRPCClient) SetVolumeID(pv runtime.Unstructured, volumeID string) (runtime.Unstructured, error) {
|
||||
encodedPV, err := json.Marshal(pv.UnstructuredContent())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := &proto.SetVolumeIDRequest{
|
||||
Plugin: c.plugin,
|
||||
PersistentVolume: encodedPV,
|
||||
VolumeID: volumeID,
|
||||
}
|
||||
|
||||
resp, err := c.grpcClient.SetVolumeID(context.Background(), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var updatedPV unstructured.Unstructured
|
||||
if err := json.Unmarshal(resp.PersistentVolume, &updatedPV); err != nil {
|
||||
return nil, err
|
||||
|
||||
}
|
||||
|
||||
return &updatedPV, nil
|
||||
}
|
||||
@@ -14,189 +14,19 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package plugin
|
||||
package framework
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
proto "github.com/heptio/velero/pkg/plugin/generated"
|
||||
"github.com/heptio/velero/pkg/plugin/velero"
|
||||
)
|
||||
|
||||
// BlockStorePlugin is an implementation of go-plugin's Plugin
|
||||
// interface with support for gRPC for the cloudprovider/BlockStore
|
||||
// interface.
|
||||
type BlockStorePlugin struct {
|
||||
plugin.NetRPCUnsupportedPlugin
|
||||
*pluginBase
|
||||
}
|
||||
|
||||
// NewBlockStorePlugin constructs a BlockStorePlugin.
|
||||
func NewBlockStorePlugin(options ...pluginOption) *BlockStorePlugin {
|
||||
return &BlockStorePlugin{
|
||||
pluginBase: newPluginBase(options...),
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// client code
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// GRPCClient returns a BlockStore gRPC client.
|
||||
func (p *BlockStorePlugin) GRPCClient(c *grpc.ClientConn) (interface{}, error) {
|
||||
return newClientDispenser(p.clientLogger, c, newBlockStoreGRPCClient), nil
|
||||
}
|
||||
|
||||
// BlockStoreGRPCClient implements the cloudprovider.BlockStore interface and uses a
|
||||
// gRPC client to make calls to the plugin server.
|
||||
type BlockStoreGRPCClient struct {
|
||||
*clientBase
|
||||
grpcClient proto.BlockStoreClient
|
||||
}
|
||||
|
||||
func newBlockStoreGRPCClient(base *clientBase, clientConn *grpc.ClientConn) interface{} {
|
||||
return &BlockStoreGRPCClient{
|
||||
clientBase: base,
|
||||
grpcClient: proto.NewBlockStoreClient(clientConn),
|
||||
}
|
||||
}
|
||||
|
||||
// Init prepares the BlockStore for usage using the provided map of
|
||||
// configuration key-value pairs. It returns an error if the BlockStore
|
||||
// cannot be initialized from the provided config.
|
||||
func (c *BlockStoreGRPCClient) Init(config map[string]string) error {
|
||||
_, err := c.grpcClient.Init(context.Background(), &proto.InitRequest{Plugin: c.plugin, Config: config})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// CreateVolumeFromSnapshot creates a new block volume, initialized from the provided snapshot,
|
||||
// and with the specified type and IOPS (if using provisioned IOPS).
|
||||
func (c *BlockStoreGRPCClient) CreateVolumeFromSnapshot(snapshotID, volumeType, volumeAZ string, iops *int64) (string, error) {
|
||||
req := &proto.CreateVolumeRequest{
|
||||
Plugin: c.plugin,
|
||||
SnapshotID: snapshotID,
|
||||
VolumeType: volumeType,
|
||||
VolumeAZ: volumeAZ,
|
||||
}
|
||||
|
||||
if iops == nil {
|
||||
req.Iops = 0
|
||||
} else {
|
||||
req.Iops = *iops
|
||||
}
|
||||
|
||||
res, err := c.grpcClient.CreateVolumeFromSnapshot(context.Background(), req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return res.VolumeID, nil
|
||||
}
|
||||
|
||||
// GetVolumeInfo returns the type and IOPS (if using provisioned IOPS) for a specified block
|
||||
// volume.
|
||||
func (c *BlockStoreGRPCClient) GetVolumeInfo(volumeID, volumeAZ string) (string, *int64, error) {
|
||||
res, err := c.grpcClient.GetVolumeInfo(context.Background(), &proto.GetVolumeInfoRequest{Plugin: c.plugin, VolumeID: volumeID, VolumeAZ: volumeAZ})
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
var iops *int64
|
||||
if res.Iops != 0 {
|
||||
iops = &res.Iops
|
||||
}
|
||||
|
||||
return res.VolumeType, iops, nil
|
||||
}
|
||||
|
||||
// CreateSnapshot creates a snapshot of the specified block volume, and applies the provided
|
||||
// set of tags to the snapshot.
|
||||
func (c *BlockStoreGRPCClient) CreateSnapshot(volumeID, volumeAZ string, tags map[string]string) (string, error) {
|
||||
req := &proto.CreateSnapshotRequest{
|
||||
Plugin: c.plugin,
|
||||
VolumeID: volumeID,
|
||||
VolumeAZ: volumeAZ,
|
||||
Tags: tags,
|
||||
}
|
||||
|
||||
res, err := c.grpcClient.CreateSnapshot(context.Background(), req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return res.SnapshotID, nil
|
||||
}
|
||||
|
||||
// DeleteSnapshot deletes the specified volume snapshot.
|
||||
func (c *BlockStoreGRPCClient) DeleteSnapshot(snapshotID string) error {
|
||||
_, err := c.grpcClient.DeleteSnapshot(context.Background(), &proto.DeleteSnapshotRequest{Plugin: c.plugin, SnapshotID: snapshotID})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BlockStoreGRPCClient) GetVolumeID(pv runtime.Unstructured) (string, error) {
|
||||
encodedPV, err := json.Marshal(pv.UnstructuredContent())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
req := &proto.GetVolumeIDRequest{
|
||||
Plugin: c.plugin,
|
||||
PersistentVolume: encodedPV,
|
||||
}
|
||||
|
||||
resp, err := c.grpcClient.GetVolumeID(context.Background(), req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return resp.VolumeID, nil
|
||||
}
|
||||
|
||||
func (c *BlockStoreGRPCClient) SetVolumeID(pv runtime.Unstructured, volumeID string) (runtime.Unstructured, error) {
|
||||
encodedPV, err := json.Marshal(pv.UnstructuredContent())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := &proto.SetVolumeIDRequest{
|
||||
Plugin: c.plugin,
|
||||
PersistentVolume: encodedPV,
|
||||
VolumeID: volumeID,
|
||||
}
|
||||
|
||||
resp, err := c.grpcClient.SetVolumeID(context.Background(), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var updatedPV unstructured.Unstructured
|
||||
if err := json.Unmarshal(resp.PersistentVolume, &updatedPV); err != nil {
|
||||
return nil, err
|
||||
|
||||
}
|
||||
|
||||
return &updatedPV, nil
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// server code
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// GRPCServer registers a BlockStore gRPC server.
|
||||
func (p *BlockStorePlugin) GRPCServer(s *grpc.Server) error {
|
||||
proto.RegisterBlockStoreServer(s, &BlockStoreGRPCServer{mux: p.serverMux})
|
||||
return nil
|
||||
}
|
||||
|
||||
// BlockStoreGRPCServer implements the proto-generated BlockStoreServer interface, and accepts
|
||||
// gRPC calls and forwards them to an implementation of the pluggable interface.
|
||||
type BlockStoreGRPCServer struct {
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2018 the Heptio Ark contributors.
|
||||
Copyright 2018, 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -13,7 +13,8 @@ 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 plugin
|
||||
|
||||
package framework
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -27,7 +28,7 @@ type clientBase struct {
|
||||
}
|
||||
|
||||
type ClientDispenser interface {
|
||||
clientFor(name string) interface{}
|
||||
ClientFor(name string) interface{}
|
||||
}
|
||||
|
||||
// clientDispenser supports the initialization and retrieval of multiple implementations for a single plugin kind, such as
|
||||
@@ -55,9 +56,9 @@ func newClientDispenser(logger logrus.FieldLogger, clientConn *grpc.ClientConn,
|
||||
}
|
||||
}
|
||||
|
||||
// clientFor returns a gRPC client stub for the implementation of a plugin named name. If the client stub does not
|
||||
// ClientFor returns a gRPC client stub for the implementation of a plugin named name. If the client stub does not
|
||||
// currently exist, clientFor creates it.
|
||||
func (cd *clientDispenser) clientFor(name string) interface{} {
|
||||
func (cd *clientDispenser) ClientFor(name string) interface{} {
|
||||
if client, found := cd.clients[name]; found {
|
||||
return client
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2018 the Heptio Ark contributors.
|
||||
Copyright 2018, 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -13,7 +13,7 @@ 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 plugin
|
||||
package framework
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -61,7 +61,7 @@ func TestClientFor(t *testing.T) {
|
||||
|
||||
cd := newClientDispenser(logger, clientConn, initFunc)
|
||||
|
||||
actual := cd.clientFor("pod")
|
||||
actual := cd.ClientFor("pod")
|
||||
require.IsType(t, &fakeClient{}, actual)
|
||||
typed := actual.(*fakeClient)
|
||||
assert.Equal(t, 1, count)
|
||||
@@ -74,7 +74,7 @@ func TestClientFor(t *testing.T) {
|
||||
assert.Equal(t, clientConn, typed.clientConn)
|
||||
|
||||
// Make sure we reuse a previous client
|
||||
actual = cd.clientFor("pod")
|
||||
actual = cd.ClientFor("pod")
|
||||
require.IsType(t, &fakeClient{}, actual)
|
||||
typed = actual.(*fakeClient)
|
||||
assert.Equal(t, 1, count)
|
||||
3
pkg/plugin/framework/doc.go
Normal file
3
pkg/plugin/framework/doc.go
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package framework is the common package that any plugin client
|
||||
// will need to import, for example, both plugin authors and Velero core.
|
||||
package framework
|
||||
93
pkg/plugin/framework/examples_test.go
Normal file
93
pkg/plugin/framework/examples_test.go
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
Copyright 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package framework
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func ExampleNewServer_blockStore() {
|
||||
NewServer(). // call the server
|
||||
RegisterBlockStore("example-blockstore", newBlockStore). // register the plugin
|
||||
Serve() // serve the plugin
|
||||
}
|
||||
|
||||
func newBlockStore(logger logrus.FieldLogger) (interface{}, error) {
|
||||
return &BlockStore{FieldLogger: logger}, nil
|
||||
}
|
||||
|
||||
type BlockStore struct {
|
||||
FieldLogger logrus.FieldLogger
|
||||
}
|
||||
|
||||
// Implement all methods for the BlockStore interface...
|
||||
func (b *BlockStore) Init(config map[string]string) error {
|
||||
b.FieldLogger.Infof("BlockStore.Init called")
|
||||
|
||||
// ...
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BlockStore) CreateVolumeFromSnapshot(snapshotID, volumeType, volumeAZ string, iops *int64) (volumeID string, err error) {
|
||||
b.FieldLogger.Infof("CreateVolumeFromSnapshot called")
|
||||
|
||||
// ...
|
||||
|
||||
return "volumeID", nil
|
||||
}
|
||||
|
||||
func (b *BlockStore) GetVolumeID(pv runtime.Unstructured) (string, error) {
|
||||
b.FieldLogger.Infof("GetVolumeID called")
|
||||
|
||||
// ...
|
||||
|
||||
return "volumeID", nil
|
||||
}
|
||||
|
||||
func (b *BlockStore) SetVolumeID(pv runtime.Unstructured, volumeID string) (runtime.Unstructured, error) {
|
||||
b.FieldLogger.Infof("SetVolumeID called")
|
||||
|
||||
// ...
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *BlockStore) GetVolumeInfo(volumeID, volumeAZ string) (string, *int64, error) {
|
||||
b.FieldLogger.Infof("GetVolumeInfo called")
|
||||
|
||||
// ...
|
||||
|
||||
return "volumeFilesystemType", nil, nil
|
||||
}
|
||||
|
||||
func (b *BlockStore) CreateSnapshot(volumeID, volumeAZ string, tags map[string]string) (snapshotID string, err error) {
|
||||
b.FieldLogger.Infof("CreateSnapshot called")
|
||||
|
||||
// ...
|
||||
|
||||
return "snapshotID", nil
|
||||
}
|
||||
|
||||
func (b *BlockStore) DeleteSnapshot(snapshotID string) error {
|
||||
b.FieldLogger.Infof("DeleteSnapshot called")
|
||||
|
||||
// ...
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package plugin
|
||||
package framework
|
||||
|
||||
import (
|
||||
"google.golang.org/grpc/codes"
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 the Heptio Ark contributors.
|
||||
Copyright 2017, 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package plugin
|
||||
package framework
|
||||
|
||||
import plugin "github.com/hashicorp/go-plugin"
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package plugin
|
||||
package framework
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -13,7 +13,7 @@ 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 plugin
|
||||
package framework
|
||||
|
||||
import (
|
||||
"testing"
|
||||
44
pkg/plugin/framework/object_store.go
Normal file
44
pkg/plugin/framework/object_store.go
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package framework
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
proto "github.com/heptio/velero/pkg/plugin/generated"
|
||||
)
|
||||
|
||||
// ObjectStorePlugin is an implementation of go-plugin's Plugin
|
||||
// interface with support for gRPC for the cloudprovider/ObjectStore
|
||||
// interface.
|
||||
type ObjectStorePlugin struct {
|
||||
plugin.NetRPCUnsupportedPlugin
|
||||
*pluginBase
|
||||
}
|
||||
|
||||
// GRPCClient returns an ObjectStore gRPC client.
|
||||
func (p *ObjectStorePlugin) GRPCClient(c *grpc.ClientConn) (interface{}, error) {
|
||||
return newClientDispenser(p.clientLogger, c, newObjectStoreGRPCClient), nil
|
||||
|
||||
}
|
||||
|
||||
// GRPCServer registers an ObjectStore gRPC server.
|
||||
func (p *ObjectStorePlugin) GRPCServer(s *grpc.Server) error {
|
||||
proto.RegisterObjectStoreServer(s, &ObjectStoreGRPCServer{mux: p.serverMux})
|
||||
return nil
|
||||
}
|
||||
163
pkg/plugin/framework/object_store_client.go
Normal file
163
pkg/plugin/framework/object_store_client.go
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
Copyright 2017, 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package framework
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
proto "github.com/heptio/velero/pkg/plugin/generated"
|
||||
)
|
||||
|
||||
const byteChunkSize = 16384
|
||||
|
||||
// NewObjectStorePlugin construct an ObjectStorePlugin.
|
||||
func NewObjectStorePlugin(options ...PluginOption) *ObjectStorePlugin {
|
||||
return &ObjectStorePlugin{
|
||||
pluginBase: newPluginBase(options...),
|
||||
}
|
||||
}
|
||||
|
||||
// ObjectStoreGRPCClient implements the ObjectStore interface and uses a
|
||||
// gRPC client to make calls to the plugin server.
|
||||
type ObjectStoreGRPCClient struct {
|
||||
*clientBase
|
||||
grpcClient proto.ObjectStoreClient
|
||||
}
|
||||
|
||||
func newObjectStoreGRPCClient(base *clientBase, clientConn *grpc.ClientConn) interface{} {
|
||||
return &ObjectStoreGRPCClient{
|
||||
clientBase: base,
|
||||
grpcClient: proto.NewObjectStoreClient(clientConn),
|
||||
}
|
||||
}
|
||||
|
||||
// Init prepares the ObjectStore for usage using the provided map of
|
||||
// configuration key-value pairs. It returns an error if the ObjectStore
|
||||
// cannot be initialized from the provided config.
|
||||
func (c *ObjectStoreGRPCClient) Init(config map[string]string) error {
|
||||
_, err := c.grpcClient.Init(context.Background(), &proto.InitRequest{Plugin: c.plugin, Config: config})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// PutObject creates a new object using the data in body within the specified
|
||||
// object storage bucket with the given key.
|
||||
func (c *ObjectStoreGRPCClient) PutObject(bucket, key string, body io.Reader) error {
|
||||
stream, err := c.grpcClient.PutObject(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// read from the provider io.Reader into chunks, and send each one over
|
||||
// the gRPC stream
|
||||
chunk := make([]byte, byteChunkSize)
|
||||
for {
|
||||
n, err := body.Read(chunk)
|
||||
if err == io.EOF {
|
||||
_, resErr := stream.CloseAndRecv()
|
||||
return resErr
|
||||
}
|
||||
if err != nil {
|
||||
stream.CloseSend()
|
||||
return err
|
||||
}
|
||||
|
||||
if err := stream.Send(&proto.PutObjectRequest{Plugin: c.plugin, Bucket: bucket, Key: key, Body: chunk[0:n]}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetObject retrieves the object with the given key from the specified
|
||||
// bucket in object storage.
|
||||
func (c *ObjectStoreGRPCClient) GetObject(bucket, key string) (io.ReadCloser, error) {
|
||||
stream, err := c.grpcClient.GetObject(context.Background(), &proto.GetObjectRequest{Plugin: c.plugin, Bucket: bucket, Key: key})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
receive := func() ([]byte, error) {
|
||||
data, err := stream.Recv()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data.Data, nil
|
||||
}
|
||||
|
||||
close := func() error {
|
||||
return stream.CloseSend()
|
||||
}
|
||||
|
||||
return &StreamReadCloser{receive: receive, close: close}, nil
|
||||
}
|
||||
|
||||
// ListCommonPrefixes gets a list of all object key prefixes that come
|
||||
// after the provided prefix and before the provided delimiter (this is
|
||||
// often used to simulate a directory hierarchy in object storage).
|
||||
func (c *ObjectStoreGRPCClient) ListCommonPrefixes(bucket, prefix, delimiter string) ([]string, error) {
|
||||
req := &proto.ListCommonPrefixesRequest{
|
||||
Plugin: c.plugin,
|
||||
Bucket: bucket,
|
||||
Prefix: prefix,
|
||||
Delimiter: delimiter,
|
||||
}
|
||||
|
||||
res, err := c.grpcClient.ListCommonPrefixes(context.Background(), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Prefixes, nil
|
||||
}
|
||||
|
||||
// ListObjects gets a list of all objects in bucket that have the same prefix.
|
||||
func (c *ObjectStoreGRPCClient) ListObjects(bucket, prefix string) ([]string, error) {
|
||||
res, err := c.grpcClient.ListObjects(context.Background(), &proto.ListObjectsRequest{Plugin: c.plugin, Bucket: bucket, Prefix: prefix})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Keys, nil
|
||||
}
|
||||
|
||||
// DeleteObject removes object with the specified key from the given
|
||||
// bucket.
|
||||
func (c *ObjectStoreGRPCClient) DeleteObject(bucket, key string) error {
|
||||
_, err := c.grpcClient.DeleteObject(context.Background(), &proto.DeleteObjectRequest{Plugin: c.plugin, Bucket: bucket, Key: key})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// CreateSignedURL creates a pre-signed URL for the given bucket and key that expires after ttl.
|
||||
func (c *ObjectStoreGRPCClient) CreateSignedURL(bucket, key string, ttl time.Duration) (string, error) {
|
||||
res, err := c.grpcClient.CreateSignedURL(context.Background(), &proto.CreateSignedURLRequest{
|
||||
Plugin: c.plugin,
|
||||
Bucket: bucket,
|
||||
Key: key,
|
||||
Ttl: int64(ttl),
|
||||
})
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return res.Url, nil
|
||||
}
|
||||
@@ -14,185 +14,19 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package plugin
|
||||
package framework
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
proto "github.com/heptio/velero/pkg/plugin/generated"
|
||||
"github.com/heptio/velero/pkg/plugin/velero"
|
||||
)
|
||||
|
||||
const byteChunkSize = 16384
|
||||
|
||||
// ObjectStorePlugin is an implementation of go-plugin's Plugin
|
||||
// interface with support for gRPC for the cloudprovider/ObjectStore
|
||||
// interface.
|
||||
type ObjectStorePlugin struct {
|
||||
plugin.NetRPCUnsupportedPlugin
|
||||
*pluginBase
|
||||
}
|
||||
|
||||
// NewObjectStorePlugin construct an ObjectStorePlugin.
|
||||
func NewObjectStorePlugin(options ...pluginOption) *ObjectStorePlugin {
|
||||
return &ObjectStorePlugin{
|
||||
pluginBase: newPluginBase(options...),
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// client code
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// GRPCClient returns an ObjectStore gRPC client.
|
||||
func (p *ObjectStorePlugin) GRPCClient(c *grpc.ClientConn) (interface{}, error) {
|
||||
return newClientDispenser(p.clientLogger, c, newObjectStoreGRPCClient), nil
|
||||
|
||||
}
|
||||
|
||||
// ObjectStoreGRPCClient implements the ObjectStore interface and uses a
|
||||
// gRPC client to make calls to the plugin server.
|
||||
type ObjectStoreGRPCClient struct {
|
||||
*clientBase
|
||||
grpcClient proto.ObjectStoreClient
|
||||
}
|
||||
|
||||
func newObjectStoreGRPCClient(base *clientBase, clientConn *grpc.ClientConn) interface{} {
|
||||
return &ObjectStoreGRPCClient{
|
||||
clientBase: base,
|
||||
grpcClient: proto.NewObjectStoreClient(clientConn),
|
||||
}
|
||||
}
|
||||
|
||||
// Init prepares the ObjectStore for usage using the provided map of
|
||||
// configuration key-value pairs. It returns an error if the ObjectStore
|
||||
// cannot be initialized from the provided config.
|
||||
func (c *ObjectStoreGRPCClient) Init(config map[string]string) error {
|
||||
_, err := c.grpcClient.Init(context.Background(), &proto.InitRequest{Plugin: c.plugin, Config: config})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// PutObject creates a new object using the data in body within the specified
|
||||
// object storage bucket with the given key.
|
||||
func (c *ObjectStoreGRPCClient) PutObject(bucket, key string, body io.Reader) error {
|
||||
stream, err := c.grpcClient.PutObject(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// read from the provider io.Reader into chunks, and send each one over
|
||||
// the gRPC stream
|
||||
chunk := make([]byte, byteChunkSize)
|
||||
for {
|
||||
n, err := body.Read(chunk)
|
||||
if err == io.EOF {
|
||||
_, resErr := stream.CloseAndRecv()
|
||||
return resErr
|
||||
}
|
||||
if err != nil {
|
||||
stream.CloseSend()
|
||||
return err
|
||||
}
|
||||
|
||||
if err := stream.Send(&proto.PutObjectRequest{Plugin: c.plugin, Bucket: bucket, Key: key, Body: chunk[0:n]}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetObject retrieves the object with the given key from the specified
|
||||
// bucket in object storage.
|
||||
func (c *ObjectStoreGRPCClient) GetObject(bucket, key string) (io.ReadCloser, error) {
|
||||
stream, err := c.grpcClient.GetObject(context.Background(), &proto.GetObjectRequest{Plugin: c.plugin, Bucket: bucket, Key: key})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
receive := func() ([]byte, error) {
|
||||
data, err := stream.Recv()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data.Data, nil
|
||||
}
|
||||
|
||||
close := func() error {
|
||||
return stream.CloseSend()
|
||||
}
|
||||
|
||||
return &StreamReadCloser{receive: receive, close: close}, nil
|
||||
}
|
||||
|
||||
// ListCommonPrefixes gets a list of all object key prefixes that come
|
||||
// after the provided prefix and before the provided delimiter (this is
|
||||
// often used to simulate a directory hierarchy in object storage).
|
||||
func (c *ObjectStoreGRPCClient) ListCommonPrefixes(bucket, prefix, delimiter string) ([]string, error) {
|
||||
req := &proto.ListCommonPrefixesRequest{
|
||||
Plugin: c.plugin,
|
||||
Bucket: bucket,
|
||||
Prefix: prefix,
|
||||
Delimiter: delimiter,
|
||||
}
|
||||
|
||||
res, err := c.grpcClient.ListCommonPrefixes(context.Background(), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Prefixes, nil
|
||||
}
|
||||
|
||||
// ListObjects gets a list of all objects in bucket that have the same prefix.
|
||||
func (c *ObjectStoreGRPCClient) ListObjects(bucket, prefix string) ([]string, error) {
|
||||
res, err := c.grpcClient.ListObjects(context.Background(), &proto.ListObjectsRequest{Plugin: c.plugin, Bucket: bucket, Prefix: prefix})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Keys, nil
|
||||
}
|
||||
|
||||
// DeleteObject removes object with the specified key from the given
|
||||
// bucket.
|
||||
func (c *ObjectStoreGRPCClient) DeleteObject(bucket, key string) error {
|
||||
_, err := c.grpcClient.DeleteObject(context.Background(), &proto.DeleteObjectRequest{Plugin: c.plugin, Bucket: bucket, Key: key})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// CreateSignedURL creates a pre-signed URL for the given bucket and key that expires after ttl.
|
||||
func (c *ObjectStoreGRPCClient) CreateSignedURL(bucket, key string, ttl time.Duration) (string, error) {
|
||||
res, err := c.grpcClient.CreateSignedURL(context.Background(), &proto.CreateSignedURLRequest{
|
||||
Plugin: c.plugin,
|
||||
Bucket: bucket,
|
||||
Key: key,
|
||||
Ttl: int64(ttl),
|
||||
})
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return res.Url, nil
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// server code
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// GRPCServer registers an ObjectStore gRPC server.
|
||||
func (p *ObjectStorePlugin) GRPCServer(s *grpc.Server) error {
|
||||
proto.RegisterObjectStoreServer(s, &ObjectStoreGRPCServer{mux: p.serverMux})
|
||||
return nil
|
||||
}
|
||||
|
||||
// ObjectStoreGRPCServer implements the proto-generated ObjectStoreServer interface, and accepts
|
||||
// gRPC calls and forwards them to an implementation of the pluggable interface.
|
||||
type ObjectStoreGRPCServer struct {
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2018 the Heptio Ark contributors.
|
||||
Copyright 2018, 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -14,16 +14,18 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package plugin
|
||||
package framework
|
||||
|
||||
import "github.com/sirupsen/logrus"
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type pluginBase struct {
|
||||
clientLogger logrus.FieldLogger
|
||||
*serverMux
|
||||
}
|
||||
|
||||
func newPluginBase(options ...pluginOption) *pluginBase {
|
||||
func newPluginBase(options ...PluginOption) *pluginBase {
|
||||
base := new(pluginBase)
|
||||
for _, option := range options {
|
||||
option(base)
|
||||
@@ -31,15 +33,15 @@ func newPluginBase(options ...pluginOption) *pluginBase {
|
||||
return base
|
||||
}
|
||||
|
||||
type pluginOption func(base *pluginBase)
|
||||
type PluginOption func(base *pluginBase)
|
||||
|
||||
func clientLogger(logger logrus.FieldLogger) pluginOption {
|
||||
func ClientLogger(logger logrus.FieldLogger) PluginOption {
|
||||
return func(base *pluginBase) {
|
||||
base.clientLogger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func serverLogger(logger logrus.FieldLogger) pluginOption {
|
||||
func serverLogger(logger logrus.FieldLogger) PluginOption {
|
||||
return func(base *pluginBase) {
|
||||
base.serverMux = newServerMux(logger)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2018 the Heptio Ark contributors.
|
||||
Copyright 2018, 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -13,7 +13,7 @@ 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 plugin
|
||||
package framework
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
func TestClientLogger(t *testing.T) {
|
||||
base := &pluginBase{}
|
||||
logger := test.NewLogger()
|
||||
f := clientLogger(logger)
|
||||
f := ClientLogger(logger)
|
||||
f(base)
|
||||
assert.Equal(t, logger, base.clientLogger)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2018 the Heptio Ark contributors.
|
||||
Copyright 2018, 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -13,7 +13,8 @@ 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 plugin
|
||||
|
||||
package framework
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2018 the Heptio Ark contributors.
|
||||
Copyright 2018, 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -13,7 +13,7 @@ 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 plugin
|
||||
package framework
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2018 the Heptio Ark contributors.
|
||||
Copyright 2018, 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -13,7 +13,8 @@ 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 plugin
|
||||
|
||||
package framework
|
||||
|
||||
import (
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
@@ -24,7 +25,7 @@ import (
|
||||
proto "github.com/heptio/velero/pkg/plugin/generated"
|
||||
)
|
||||
|
||||
// PluginIdenitifer uniquely identifies a plugin by command, kind, and name.
|
||||
// PluginIdentifier uniquely identifies a plugin by command, kind, and name.
|
||||
type PluginIdentifier struct {
|
||||
Command string
|
||||
Kind PluginKind
|
||||
43
pkg/plugin/framework/restore_item_action.go
Normal file
43
pkg/plugin/framework/restore_item_action.go
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package framework
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
proto "github.com/heptio/velero/pkg/plugin/generated"
|
||||
)
|
||||
|
||||
// RestoreItemActionPlugin is an implementation of go-plugin's Plugin
|
||||
// interface with support for gRPC for the restore/ItemAction
|
||||
// interface.
|
||||
type RestoreItemActionPlugin struct {
|
||||
plugin.NetRPCUnsupportedPlugin
|
||||
*pluginBase
|
||||
}
|
||||
|
||||
// GRPCClient returns a RestoreItemAction gRPC client.
|
||||
func (p *RestoreItemActionPlugin) GRPCClient(c *grpc.ClientConn) (interface{}, error) {
|
||||
return newClientDispenser(p.clientLogger, c, newRestoreItemActionGRPCClient), nil
|
||||
}
|
||||
|
||||
// GRPCServer registers a RestoreItemAction gRPC server.
|
||||
func (p *RestoreItemActionPlugin) GRPCServer(s *grpc.Server) error {
|
||||
proto.RegisterRestoreItemActionServer(s, &RestoreItemActionGRPCServer{mux: p.serverMux})
|
||||
return nil
|
||||
}
|
||||
111
pkg/plugin/framework/restore_item_action_client.go
Normal file
111
pkg/plugin/framework/restore_item_action_client.go
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
Copyright 2017, 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package framework
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
proto "github.com/heptio/velero/pkg/plugin/generated"
|
||||
"github.com/heptio/velero/pkg/plugin/velero"
|
||||
)
|
||||
|
||||
var _ velero.RestoreItemAction = &RestoreItemActionGRPCClient{}
|
||||
|
||||
// NewRestoreItemActionPlugin constructs a RestoreItemActionPlugin.
|
||||
func NewRestoreItemActionPlugin(options ...PluginOption) *RestoreItemActionPlugin {
|
||||
return &RestoreItemActionPlugin{
|
||||
pluginBase: newPluginBase(options...),
|
||||
}
|
||||
}
|
||||
|
||||
// RestoreItemActionGRPCClient implements the backup/ItemAction interface and uses a
|
||||
// gRPC client to make calls to the plugin server.
|
||||
type RestoreItemActionGRPCClient struct {
|
||||
*clientBase
|
||||
grpcClient proto.RestoreItemActionClient
|
||||
}
|
||||
|
||||
func newRestoreItemActionGRPCClient(base *clientBase, clientConn *grpc.ClientConn) interface{} {
|
||||
return &RestoreItemActionGRPCClient{
|
||||
clientBase: base,
|
||||
grpcClient: proto.NewRestoreItemActionClient(clientConn),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RestoreItemActionGRPCClient) AppliesTo() (velero.ResourceSelector, error) {
|
||||
res, err := c.grpcClient.AppliesTo(context.Background(), &proto.AppliesToRequest{Plugin: c.plugin})
|
||||
if err != nil {
|
||||
return velero.ResourceSelector{}, err
|
||||
}
|
||||
|
||||
return velero.ResourceSelector{
|
||||
IncludedNamespaces: res.IncludedNamespaces,
|
||||
ExcludedNamespaces: res.ExcludedNamespaces,
|
||||
IncludedResources: res.IncludedResources,
|
||||
ExcludedResources: res.ExcludedResources,
|
||||
LabelSelector: res.Selector,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *RestoreItemActionGRPCClient) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) {
|
||||
itemJSON, err := json.Marshal(input.Item.UnstructuredContent())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
itemFromBackupJSON, err := json.Marshal(input.ItemFromBackup.UnstructuredContent())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
restoreJSON, err := json.Marshal(input.Restore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := &proto.RestoreExecuteRequest{
|
||||
Plugin: c.plugin,
|
||||
Item: itemJSON,
|
||||
ItemFromBackup: itemFromBackupJSON,
|
||||
Restore: restoreJSON,
|
||||
}
|
||||
|
||||
res, err := c.grpcClient.Execute(context.Background(), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var updatedItem unstructured.Unstructured
|
||||
if err := json.Unmarshal(res.Item, &updatedItem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var warning error
|
||||
if res.Warning != "" {
|
||||
warning = errors.New(res.Warning)
|
||||
}
|
||||
|
||||
return &velero.RestoreItemActionExecuteOutput{
|
||||
UpdatedItem: &updatedItem,
|
||||
Warning: warning,
|
||||
}, nil
|
||||
}
|
||||
130
pkg/plugin/framework/restore_item_action_server.go
Normal file
130
pkg/plugin/framework/restore_item_action_server.go
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
Copyright 2017, 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package framework
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
proto "github.com/heptio/velero/pkg/plugin/generated"
|
||||
"github.com/heptio/velero/pkg/plugin/velero"
|
||||
)
|
||||
|
||||
// RestoreItemActionGRPCServer implements the proto-generated RestoreItemActionServer interface, and accepts
|
||||
// gRPC calls and forwards them to an implementation of the pluggable interface.
|
||||
type RestoreItemActionGRPCServer struct {
|
||||
mux *serverMux
|
||||
}
|
||||
|
||||
func (s *RestoreItemActionGRPCServer) getImpl(name string) (velero.RestoreItemAction, error) {
|
||||
impl, err := s.mux.getHandler(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
itemAction, ok := impl.(velero.RestoreItemAction)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("%T is not a restore item action", impl)
|
||||
}
|
||||
|
||||
return itemAction, nil
|
||||
}
|
||||
|
||||
func (s *RestoreItemActionGRPCServer) AppliesTo(ctx context.Context, req *proto.AppliesToRequest) (response *proto.AppliesToResponse, err error) {
|
||||
defer func() {
|
||||
if recoveredErr := handlePanic(recover()); recoveredErr != nil {
|
||||
err = recoveredErr
|
||||
}
|
||||
}()
|
||||
|
||||
impl, err := s.getImpl(req.Plugin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
appliesTo, err := impl.AppliesTo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &proto.AppliesToResponse{
|
||||
IncludedNamespaces: appliesTo.IncludedNamespaces,
|
||||
ExcludedNamespaces: appliesTo.ExcludedNamespaces,
|
||||
IncludedResources: appliesTo.IncludedResources,
|
||||
ExcludedResources: appliesTo.ExcludedResources,
|
||||
Selector: appliesTo.LabelSelector,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *RestoreItemActionGRPCServer) Execute(ctx context.Context, req *proto.RestoreExecuteRequest) (response *proto.RestoreExecuteResponse, err error) {
|
||||
defer func() {
|
||||
if recoveredErr := handlePanic(recover()); recoveredErr != nil {
|
||||
err = recoveredErr
|
||||
}
|
||||
}()
|
||||
|
||||
impl, err := s.getImpl(req.Plugin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
item unstructured.Unstructured
|
||||
itemFromBackup unstructured.Unstructured
|
||||
restoreObj api.Restore
|
||||
)
|
||||
|
||||
if err := json.Unmarshal(req.Item, &item); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(req.ItemFromBackup, &itemFromBackup); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(req.Restore, &restoreObj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
executeOutput, err := impl.Execute(&velero.RestoreItemActionExecuteInput{
|
||||
Item: &item,
|
||||
ItemFromBackup: &itemFromBackup,
|
||||
Restore: &restoreObj,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updatedItem, err := json.Marshal(executeOutput.UpdatedItem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var warnMessage string
|
||||
if executeOutput.Warning != nil {
|
||||
warnMessage = executeOutput.Warning.Error()
|
||||
}
|
||||
|
||||
return &proto.RestoreExecuteResponse{
|
||||
Item: updatedItem,
|
||||
Warning: warnMessage,
|
||||
}, nil
|
||||
}
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package plugin
|
||||
package framework
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2018 the Heptio Ark contributors.
|
||||
Copyright 2018, 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -13,7 +13,8 @@ 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 plugin
|
||||
|
||||
package framework
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 the Heptio Ark contributors.
|
||||
Copyright 2017, 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package plugin
|
||||
package framework
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 the Heptio Ark contributors.
|
||||
Copyright 2017, 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package plugin
|
||||
package framework
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/heptio/velero/pkg/plugin/framework"
|
||||
"github.com/heptio/velero/pkg/plugin/velero"
|
||||
)
|
||||
|
||||
@@ -86,12 +87,12 @@ func (m *manager) CleanupClients() {
|
||||
|
||||
// getRestartableProcess returns a restartableProcess for a plugin identified by kind and name, creating a
|
||||
// restartableProcess if it is the first time it has been requested.
|
||||
func (m *manager) getRestartableProcess(kind PluginKind, name string) (RestartableProcess, error) {
|
||||
func (m *manager) getRestartableProcess(kind framework.PluginKind, name string) (RestartableProcess, error) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
logger := m.logger.WithFields(logrus.Fields{
|
||||
"kind": PluginKindObjectStore.String(),
|
||||
"kind": framework.PluginKindObjectStore.String(),
|
||||
"name": name,
|
||||
})
|
||||
logger.Debug("looking for plugin in registry")
|
||||
@@ -123,7 +124,7 @@ func (m *manager) getRestartableProcess(kind PluginKind, name string) (Restartab
|
||||
|
||||
// GetObjectStore returns a restartableObjectStore for name.
|
||||
func (m *manager) GetObjectStore(name string) (velero.ObjectStore, error) {
|
||||
restartableProcess, err := m.getRestartableProcess(PluginKindObjectStore, name)
|
||||
restartableProcess, err := m.getRestartableProcess(framework.PluginKindObjectStore, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -135,7 +136,7 @@ func (m *manager) GetObjectStore(name string) (velero.ObjectStore, error) {
|
||||
|
||||
// GetBlockStore returns a restartableBlockStore for name.
|
||||
func (m *manager) GetBlockStore(name string) (velero.BlockStore, error) {
|
||||
restartableProcess, err := m.getRestartableProcess(PluginKindBlockStore, name)
|
||||
restartableProcess, err := m.getRestartableProcess(framework.PluginKindBlockStore, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -147,7 +148,7 @@ func (m *manager) GetBlockStore(name string) (velero.BlockStore, error) {
|
||||
|
||||
// GetBackupItemActions returns all backup item actions as restartableBackupItemActions.
|
||||
func (m *manager) GetBackupItemActions() ([]velero.BackupItemAction, error) {
|
||||
list := m.registry.List(PluginKindBackupItemAction)
|
||||
list := m.registry.List(framework.PluginKindBackupItemAction)
|
||||
|
||||
actions := make([]velero.BackupItemAction, 0, len(list))
|
||||
|
||||
@@ -167,7 +168,7 @@ func (m *manager) GetBackupItemActions() ([]velero.BackupItemAction, error) {
|
||||
|
||||
// GetBackupItemAction returns a restartableBackupItemAction for name.
|
||||
func (m *manager) GetBackupItemAction(name string) (velero.BackupItemAction, error) {
|
||||
restartableProcess, err := m.getRestartableProcess(PluginKindBackupItemAction, name)
|
||||
restartableProcess, err := m.getRestartableProcess(framework.PluginKindBackupItemAction, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -178,7 +179,7 @@ func (m *manager) GetBackupItemAction(name string) (velero.BackupItemAction, err
|
||||
|
||||
// GetRestoreItemActions returns all restore item actions as restartableRestoreItemActions.
|
||||
func (m *manager) GetRestoreItemActions() ([]velero.RestoreItemAction, error) {
|
||||
list := m.registry.List(PluginKindRestoreItemAction)
|
||||
list := m.registry.List(framework.PluginKindRestoreItemAction)
|
||||
|
||||
actions := make([]velero.RestoreItemAction, 0, len(list))
|
||||
|
||||
@@ -198,7 +199,7 @@ func (m *manager) GetRestoreItemActions() ([]velero.RestoreItemAction, error) {
|
||||
|
||||
// GetRestoreItemAction returns a restartableRestoreItemAction for name.
|
||||
func (m *manager) GetRestoreItemAction(name string) (velero.RestoreItemAction, error) {
|
||||
restartableProcess, err := m.getRestartableProcess(PluginKindRestoreItemAction, name)
|
||||
restartableProcess, err := m.getRestartableProcess(framework.PluginKindRestoreItemAction, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/heptio/velero/pkg/plugin/framework"
|
||||
"github.com/heptio/velero/pkg/util/test"
|
||||
)
|
||||
|
||||
@@ -38,16 +39,16 @@ func (r *mockRegistry) DiscoverPlugins() error {
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (r *mockRegistry) List(kind PluginKind) []PluginIdentifier {
|
||||
func (r *mockRegistry) List(kind framework.PluginKind) []framework.PluginIdentifier {
|
||||
args := r.Called(kind)
|
||||
return args.Get(0).([]PluginIdentifier)
|
||||
return args.Get(0).([]framework.PluginIdentifier)
|
||||
}
|
||||
|
||||
func (r *mockRegistry) Get(kind PluginKind, name string) (PluginIdentifier, error) {
|
||||
func (r *mockRegistry) Get(kind framework.PluginKind, name string) (framework.PluginIdentifier, error) {
|
||||
args := r.Called(kind, name)
|
||||
var id PluginIdentifier
|
||||
var id framework.PluginIdentifier
|
||||
if args.Get(0) != nil {
|
||||
id = args.Get(0).(PluginIdentifier)
|
||||
id = args.Get(0).(framework.PluginIdentifier)
|
||||
}
|
||||
return id, args.Error(1)
|
||||
}
|
||||
@@ -120,7 +121,7 @@ func TestGetRestartableProcess(t *testing.T) {
|
||||
m.restartableProcessFactory = factory
|
||||
|
||||
// Test 1: registry error
|
||||
pluginKind := PluginKindBackupItemAction
|
||||
pluginKind := framework.PluginKindBackupItemAction
|
||||
pluginName := "pod"
|
||||
registry.On("Get", pluginKind, pluginName).Return(nil, errors.Errorf("registry")).Once()
|
||||
rp, err := m.getRestartableProcess(pluginKind, pluginName)
|
||||
@@ -128,7 +129,7 @@ func TestGetRestartableProcess(t *testing.T) {
|
||||
assert.EqualError(t, err, "registry")
|
||||
|
||||
// Test 2: registry ok, factory error
|
||||
podID := PluginIdentifier{
|
||||
podID := framework.PluginIdentifier{
|
||||
Command: "/command",
|
||||
Kind: pluginKind,
|
||||
Name: pluginName,
|
||||
@@ -174,14 +175,14 @@ func TestCleanupClients(t *testing.T) {
|
||||
|
||||
func TestGetObjectStore(t *testing.T) {
|
||||
getPluginTest(t,
|
||||
PluginKindObjectStore,
|
||||
framework.PluginKindObjectStore,
|
||||
"aws",
|
||||
func(m Manager, name string) (interface{}, error) {
|
||||
return m.GetObjectStore(name)
|
||||
},
|
||||
func(name string, sharedPluginProcess RestartableProcess) interface{} {
|
||||
return &restartableObjectStore{
|
||||
key: kindAndName{kind: PluginKindObjectStore, name: name},
|
||||
key: kindAndName{kind: framework.PluginKindObjectStore, name: name},
|
||||
sharedPluginProcess: sharedPluginProcess,
|
||||
}
|
||||
},
|
||||
@@ -191,14 +192,14 @@ func TestGetObjectStore(t *testing.T) {
|
||||
|
||||
func TestGetBlockStore(t *testing.T) {
|
||||
getPluginTest(t,
|
||||
PluginKindBlockStore,
|
||||
framework.PluginKindBlockStore,
|
||||
"aws",
|
||||
func(m Manager, name string) (interface{}, error) {
|
||||
return m.GetBlockStore(name)
|
||||
},
|
||||
func(name string, sharedPluginProcess RestartableProcess) interface{} {
|
||||
return &restartableBlockStore{
|
||||
key: kindAndName{kind: PluginKindBlockStore, name: name},
|
||||
key: kindAndName{kind: framework.PluginKindBlockStore, name: name},
|
||||
sharedPluginProcess: sharedPluginProcess,
|
||||
}
|
||||
},
|
||||
@@ -208,14 +209,14 @@ func TestGetBlockStore(t *testing.T) {
|
||||
|
||||
func TestGetBackupItemAction(t *testing.T) {
|
||||
getPluginTest(t,
|
||||
PluginKindBackupItemAction,
|
||||
framework.PluginKindBackupItemAction,
|
||||
"pod",
|
||||
func(m Manager, name string) (interface{}, error) {
|
||||
return m.GetBackupItemAction(name)
|
||||
},
|
||||
func(name string, sharedPluginProcess RestartableProcess) interface{} {
|
||||
return &restartableBackupItemAction{
|
||||
key: kindAndName{kind: PluginKindBackupItemAction, name: name},
|
||||
key: kindAndName{kind: framework.PluginKindBackupItemAction, name: name},
|
||||
sharedPluginProcess: sharedPluginProcess,
|
||||
}
|
||||
},
|
||||
@@ -225,14 +226,14 @@ func TestGetBackupItemAction(t *testing.T) {
|
||||
|
||||
func TestGetRestoreItemAction(t *testing.T) {
|
||||
getPluginTest(t,
|
||||
PluginKindRestoreItemAction,
|
||||
framework.PluginKindRestoreItemAction,
|
||||
"pod",
|
||||
func(m Manager, name string) (interface{}, error) {
|
||||
return m.GetRestoreItemAction(name)
|
||||
},
|
||||
func(name string, sharedPluginProcess RestartableProcess) interface{} {
|
||||
return &restartableRestoreItemAction{
|
||||
key: kindAndName{kind: PluginKindRestoreItemAction, name: name},
|
||||
key: kindAndName{kind: framework.PluginKindRestoreItemAction, name: name},
|
||||
sharedPluginProcess: sharedPluginProcess,
|
||||
}
|
||||
},
|
||||
@@ -242,7 +243,7 @@ func TestGetRestoreItemAction(t *testing.T) {
|
||||
|
||||
func getPluginTest(
|
||||
t *testing.T,
|
||||
kind PluginKind,
|
||||
kind framework.PluginKind,
|
||||
name string,
|
||||
getPluginFunc func(m Manager, name string) (interface{}, error),
|
||||
expectedResultFunc func(name string, sharedPluginProcess RestartableProcess) interface{},
|
||||
@@ -261,7 +262,7 @@ func getPluginTest(
|
||||
|
||||
pluginKind := kind
|
||||
pluginName := name
|
||||
pluginID := PluginIdentifier{
|
||||
pluginID := framework.PluginIdentifier{
|
||||
Command: "/command",
|
||||
Kind: pluginKind,
|
||||
Name: pluginName,
|
||||
@@ -326,10 +327,10 @@ func TestGetBackupItemActions(t *testing.T) {
|
||||
defer factory.AssertExpectations(t)
|
||||
m.restartableProcessFactory = factory
|
||||
|
||||
pluginKind := PluginKindBackupItemAction
|
||||
var pluginIDs []PluginIdentifier
|
||||
pluginKind := framework.PluginKindBackupItemAction
|
||||
var pluginIDs []framework.PluginIdentifier
|
||||
for i := range tc.names {
|
||||
pluginID := PluginIdentifier{
|
||||
pluginID := framework.PluginIdentifier{
|
||||
Command: "/command",
|
||||
Kind: pluginKind,
|
||||
Name: tc.names[i],
|
||||
@@ -418,10 +419,10 @@ func TestGetRestoreItemActions(t *testing.T) {
|
||||
defer factory.AssertExpectations(t)
|
||||
m.restartableProcessFactory = factory
|
||||
|
||||
pluginKind := PluginKindRestoreItemAction
|
||||
var pluginIDs []PluginIdentifier
|
||||
pluginKind := framework.PluginKindRestoreItemAction
|
||||
var pluginIDs []framework.PluginIdentifier
|
||||
for i := range tc.names {
|
||||
pluginID := PluginIdentifier{
|
||||
pluginID := framework.PluginIdentifier{
|
||||
Command: "/command",
|
||||
Kind: pluginKind,
|
||||
Name: tc.names[i],
|
||||
|
||||
@@ -19,6 +19,8 @@ import (
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/heptio/velero/pkg/plugin/framework"
|
||||
)
|
||||
|
||||
type ProcessFactory interface {
|
||||
@@ -75,13 +77,13 @@ func (r *process) dispense(key kindAndName) (interface{}, error) {
|
||||
}
|
||||
|
||||
// Currently all plugins except for PluginLister dispense clientDispenser instances.
|
||||
if clientDispenser, ok := dispensed.(ClientDispenser); ok {
|
||||
if clientDispenser, ok := dispensed.(framework.ClientDispenser); ok {
|
||||
if key.name == "" {
|
||||
return nil, errors.Errorf("%s plugin requested but name is missing", key.kind.String())
|
||||
}
|
||||
// Get the instance that implements our plugin interface (e.g. ObjectStore) that is a gRPC-based
|
||||
// client
|
||||
dispensed = clientDispenser.clientFor(key.name)
|
||||
dispensed = clientDispenser.ClientFor(key.name)
|
||||
}
|
||||
|
||||
return dispensed, nil
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/heptio/velero/pkg/plugin/framework"
|
||||
)
|
||||
|
||||
type mockClientProtocol struct {
|
||||
@@ -47,7 +49,7 @@ type mockClientDispenser struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (cd *mockClientDispenser) clientFor(name string) interface{} {
|
||||
func (cd *mockClientDispenser) ClientFor(name string) interface{} {
|
||||
args := cd.Called(name)
|
||||
return args.Get(0)
|
||||
}
|
||||
@@ -94,17 +96,17 @@ func TestDispense(t *testing.T) {
|
||||
|
||||
key := kindAndName{}
|
||||
if tc.clientDispenser {
|
||||
key.kind = PluginKindObjectStore
|
||||
key.kind = framework.PluginKindObjectStore
|
||||
protocolClient.On("Dispense", key.kind.String()).Return(clientDispenser, tc.dispenseError)
|
||||
|
||||
if !tc.missingKeyName {
|
||||
key.name = "aws"
|
||||
client = &BackupItemActionGRPCClient{}
|
||||
clientDispenser.On("clientFor", key.name).Return(client)
|
||||
client = &framework.BackupItemActionGRPCClient{}
|
||||
clientDispenser.On("ClientFor", key.name).Return(client)
|
||||
}
|
||||
} else {
|
||||
key.kind = PluginKindPluginLister
|
||||
client = &PluginListerGRPCClient{}
|
||||
key.kind = framework.PluginKindPluginLister
|
||||
client = &framework.PluginListerGRPCClient{}
|
||||
protocolClient.On("Dispense", key.kind.String()).Return(client, tc.dispenseError)
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/heptio/velero/pkg/plugin/framework"
|
||||
"github.com/heptio/velero/pkg/util/filesystem"
|
||||
)
|
||||
|
||||
@@ -31,14 +32,14 @@ type Registry interface {
|
||||
// DiscoverPlugins discovers all available plugins.
|
||||
DiscoverPlugins() error
|
||||
// List returns all PluginIdentifiers for kind.
|
||||
List(kind PluginKind) []PluginIdentifier
|
||||
List(kind framework.PluginKind) []framework.PluginIdentifier
|
||||
// Get returns the PluginIdentifier for kind and name.
|
||||
Get(kind PluginKind, name string) (PluginIdentifier, error)
|
||||
Get(kind framework.PluginKind, name string) (framework.PluginIdentifier, error)
|
||||
}
|
||||
|
||||
// kindAndName is a convenience struct that combines a PluginKind and a name.
|
||||
type kindAndName struct {
|
||||
kind PluginKind
|
||||
kind framework.PluginKind
|
||||
name string
|
||||
}
|
||||
|
||||
@@ -51,8 +52,8 @@ type registry struct {
|
||||
|
||||
processFactory ProcessFactory
|
||||
fs filesystem.Interface
|
||||
pluginsByID map[kindAndName]PluginIdentifier
|
||||
pluginsByKind map[PluginKind][]PluginIdentifier
|
||||
pluginsByID map[kindAndName]framework.PluginIdentifier
|
||||
pluginsByKind map[framework.PluginKind][]framework.PluginIdentifier
|
||||
}
|
||||
|
||||
// NewRegistry returns a new registry.
|
||||
@@ -64,8 +65,8 @@ func NewRegistry(dir string, logger logrus.FieldLogger, logLevel logrus.Level) R
|
||||
|
||||
processFactory: newProcessFactory(),
|
||||
fs: filesystem.NewFileSystem(),
|
||||
pluginsByID: make(map[kindAndName]PluginIdentifier),
|
||||
pluginsByKind: make(map[PluginKind][]PluginIdentifier),
|
||||
pluginsByID: make(map[kindAndName]framework.PluginIdentifier),
|
||||
pluginsByKind: make(map[framework.PluginKind][]framework.PluginIdentifier),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,16 +109,16 @@ func (r *registry) discoverPlugins(commands []string) error {
|
||||
|
||||
// List returns info about all plugin binaries that implement the given
|
||||
// PluginKind.
|
||||
func (r *registry) List(kind PluginKind) []PluginIdentifier {
|
||||
func (r *registry) List(kind framework.PluginKind) []framework.PluginIdentifier {
|
||||
return r.pluginsByKind[kind]
|
||||
}
|
||||
|
||||
// Get returns info about a plugin with the given name and kind, or an
|
||||
// error if one cannot be found.
|
||||
func (r *registry) Get(kind PluginKind, name string) (PluginIdentifier, error) {
|
||||
func (r *registry) Get(kind framework.PluginKind, name string) (framework.PluginIdentifier, error) {
|
||||
p, found := r.pluginsByID[kindAndName{kind: kind, name: name}]
|
||||
if !found {
|
||||
return PluginIdentifier{}, newPluginNotFoundError(kind, name)
|
||||
return framework.PluginIdentifier{}, newPluginNotFoundError(kind, name)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
@@ -173,19 +174,19 @@ func executable(info os.FileInfo) bool {
|
||||
}
|
||||
|
||||
// listPlugins executes command, queries it for registered plugins, and returns the list of PluginIdentifiers.
|
||||
func (r *registry) listPlugins(command string) ([]PluginIdentifier, error) {
|
||||
func (r *registry) listPlugins(command string) ([]framework.PluginIdentifier, error) {
|
||||
process, err := r.processFactory.newProcess(command, r.logger, r.logLevel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer process.kill()
|
||||
|
||||
plugin, err := process.dispense(kindAndName{kind: PluginKindPluginLister})
|
||||
plugin, err := process.dispense(kindAndName{kind: framework.PluginKindPluginLister})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lister, ok := plugin.(PluginLister)
|
||||
lister, ok := plugin.(framework.PluginLister)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("%T is not a PluginLister", plugin)
|
||||
}
|
||||
@@ -194,7 +195,7 @@ func (r *registry) listPlugins(command string) ([]PluginIdentifier, error) {
|
||||
}
|
||||
|
||||
// register registers a PluginIdentifier with the registry.
|
||||
func (r *registry) register(id PluginIdentifier) error {
|
||||
func (r *registry) register(id framework.PluginIdentifier) error {
|
||||
key := kindAndName{kind: id.Kind, name: id.Name}
|
||||
if existing, found := r.pluginsByID[key]; found {
|
||||
return newDuplicatePluginRegistrationError(existing, id)
|
||||
@@ -208,12 +209,12 @@ func (r *registry) register(id PluginIdentifier) error {
|
||||
|
||||
// pluginNotFoundError indicates a plugin could not be located for kind and name.
|
||||
type pluginNotFoundError struct {
|
||||
kind PluginKind
|
||||
kind framework.PluginKind
|
||||
name string
|
||||
}
|
||||
|
||||
// newPluginNotFoundError returns a new pluginNotFoundError for kind and name.
|
||||
func newPluginNotFoundError(kind PluginKind, name string) *pluginNotFoundError {
|
||||
func newPluginNotFoundError(kind framework.PluginKind, name string) *pluginNotFoundError {
|
||||
return &pluginNotFoundError{
|
||||
kind: kind,
|
||||
name: name,
|
||||
@@ -225,11 +226,11 @@ func (e *pluginNotFoundError) Error() string {
|
||||
}
|
||||
|
||||
type duplicatePluginRegistrationError struct {
|
||||
existing PluginIdentifier
|
||||
duplicate PluginIdentifier
|
||||
existing framework.PluginIdentifier
|
||||
duplicate framework.PluginIdentifier
|
||||
}
|
||||
|
||||
func newDuplicatePluginRegistrationError(existing, duplicate PluginIdentifier) *duplicatePluginRegistrationError {
|
||||
func newDuplicatePluginRegistrationError(existing, duplicate framework.PluginIdentifier) *duplicatePluginRegistrationError {
|
||||
return &duplicatePluginRegistrationError{
|
||||
existing: existing,
|
||||
duplicate: duplicate,
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
"github.com/heptio/velero/pkg/plugin/framework"
|
||||
"github.com/heptio/velero/pkg/plugin/velero"
|
||||
)
|
||||
|
||||
@@ -35,7 +36,7 @@ type restartableBackupItemAction struct {
|
||||
// newRestartableBackupItemAction returns a new restartableBackupItemAction.
|
||||
func newRestartableBackupItemAction(name string, sharedPluginProcess RestartableProcess) *restartableBackupItemAction {
|
||||
r := &restartableBackupItemAction{
|
||||
key: kindAndName{kind: PluginKindBackupItemAction, name: name},
|
||||
key: kindAndName{kind: framework.PluginKindBackupItemAction, name: name},
|
||||
sharedPluginProcess: sharedPluginProcess,
|
||||
}
|
||||
return r
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
|
||||
v1 "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
"github.com/heptio/velero/pkg/backup/mocks"
|
||||
"github.com/heptio/velero/pkg/plugin/framework"
|
||||
"github.com/heptio/velero/pkg/plugin/velero"
|
||||
)
|
||||
|
||||
@@ -59,7 +60,7 @@ func TestRestartableGetBackupItemAction(t *testing.T) {
|
||||
defer p.AssertExpectations(t)
|
||||
|
||||
name := "pod"
|
||||
key := kindAndName{kind: PluginKindBackupItemAction, name: name}
|
||||
key := kindAndName{kind: framework.PluginKindBackupItemAction, name: name}
|
||||
p.On("getByKindAndName", key).Return(tc.plugin, tc.getError)
|
||||
|
||||
r := newRestartableBackupItemAction(name, p)
|
||||
@@ -90,7 +91,7 @@ func TestRestartableBackupItemActionGetDelegate(t *testing.T) {
|
||||
// Happy path
|
||||
p.On("resetIfNeeded").Return(nil)
|
||||
expected := new(mocks.ItemAction)
|
||||
key := kindAndName{kind: PluginKindBackupItemAction, name: name}
|
||||
key := kindAndName{kind: framework.PluginKindBackupItemAction, name: name}
|
||||
p.On("getByKindAndName", key).Return(expected, nil)
|
||||
|
||||
a, err = r.getDelegate()
|
||||
@@ -121,7 +122,7 @@ func TestRestartableBackupItemActionDelegatedFunctions(t *testing.T) {
|
||||
|
||||
runRestartableDelegateTests(
|
||||
t,
|
||||
PluginKindBackupItemAction,
|
||||
framework.PluginKindBackupItemAction,
|
||||
func(key kindAndName, p RestartableProcess) interface{} {
|
||||
return &restartableBackupItemAction{
|
||||
key: key,
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/heptio/velero/pkg/plugin/framework"
|
||||
"github.com/heptio/velero/pkg/plugin/velero"
|
||||
)
|
||||
|
||||
@@ -34,7 +35,7 @@ type restartableBlockStore struct {
|
||||
|
||||
// newRestartableBlockStore returns a new restartableBlockStore.
|
||||
func newRestartableBlockStore(name string, sharedPluginProcess RestartableProcess) *restartableBlockStore {
|
||||
key := kindAndName{kind: PluginKindBlockStore, name: name}
|
||||
key := kindAndName{kind: framework.PluginKindBlockStore, name: name}
|
||||
r := &restartableBlockStore{
|
||||
key: key,
|
||||
sharedPluginProcess: sharedPluginProcess,
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/heptio/velero/pkg/cloudprovider/mocks"
|
||||
"github.com/heptio/velero/pkg/plugin/framework"
|
||||
)
|
||||
|
||||
func TestRestartableGetBlockStore(t *testing.T) {
|
||||
@@ -58,7 +59,7 @@ func TestRestartableGetBlockStore(t *testing.T) {
|
||||
defer p.AssertExpectations(t)
|
||||
|
||||
name := "aws"
|
||||
key := kindAndName{kind: PluginKindBlockStore, name: name}
|
||||
key := kindAndName{kind: framework.PluginKindBlockStore, name: name}
|
||||
p.On("getByKindAndName", key).Return(tc.plugin, tc.getError)
|
||||
|
||||
r := &restartableBlockStore{
|
||||
@@ -83,7 +84,7 @@ func TestRestartableBlockStoreReinitialize(t *testing.T) {
|
||||
defer p.AssertExpectations(t)
|
||||
|
||||
name := "aws"
|
||||
key := kindAndName{kind: PluginKindBlockStore, name: name}
|
||||
key := kindAndName{kind: framework.PluginKindBlockStore, name: name}
|
||||
r := &restartableBlockStore{
|
||||
key: key,
|
||||
sharedPluginProcess: p,
|
||||
@@ -116,7 +117,7 @@ func TestRestartableBlockStoreGetDelegate(t *testing.T) {
|
||||
// Reset error
|
||||
p.On("resetIfNeeded").Return(errors.Errorf("reset error")).Once()
|
||||
name := "aws"
|
||||
key := kindAndName{kind: PluginKindBlockStore, name: name}
|
||||
key := kindAndName{kind: framework.PluginKindBlockStore, name: name}
|
||||
r := &restartableBlockStore{
|
||||
key: key,
|
||||
sharedPluginProcess: p,
|
||||
@@ -144,7 +145,7 @@ func TestRestartableBlockStoreInit(t *testing.T) {
|
||||
|
||||
// getBlockStore error
|
||||
name := "aws"
|
||||
key := kindAndName{kind: PluginKindBlockStore, name: name}
|
||||
key := kindAndName{kind: framework.PluginKindBlockStore, name: name}
|
||||
r := &restartableBlockStore{
|
||||
key: key,
|
||||
sharedPluginProcess: p,
|
||||
@@ -196,7 +197,7 @@ func TestRestartableBlockStoreDelegatedFunctions(t *testing.T) {
|
||||
|
||||
runRestartableDelegateTests(
|
||||
t,
|
||||
PluginKindBlockStore,
|
||||
framework.PluginKindBlockStore,
|
||||
func(key kindAndName, p RestartableProcess) interface{} {
|
||||
return &restartableBlockStore{
|
||||
key: key,
|
||||
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/heptio/velero/pkg/plugin/framework"
|
||||
)
|
||||
|
||||
type restartableDelegateTest struct {
|
||||
@@ -40,7 +42,7 @@ type mockable interface {
|
||||
|
||||
func runRestartableDelegateTests(
|
||||
t *testing.T,
|
||||
kind PluginKind,
|
||||
kind framework.PluginKind,
|
||||
newRestartable func(key kindAndName, p RestartableProcess) interface{},
|
||||
newMock func() mockable,
|
||||
tests ...restartableDelegateTest,
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/heptio/velero/pkg/plugin/framework"
|
||||
"github.com/heptio/velero/pkg/plugin/velero"
|
||||
)
|
||||
|
||||
@@ -38,7 +39,7 @@ type restartableObjectStore struct {
|
||||
|
||||
// newRestartableObjectStore returns a new restartableObjectStore.
|
||||
func newRestartableObjectStore(name string, sharedPluginProcess RestartableProcess) *restartableObjectStore {
|
||||
key := kindAndName{kind: PluginKindObjectStore, name: name}
|
||||
key := kindAndName{kind: framework.PluginKindObjectStore, name: name}
|
||||
r := &restartableObjectStore{
|
||||
key: key,
|
||||
sharedPluginProcess: sharedPluginProcess,
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
cloudprovidermocks "github.com/heptio/velero/pkg/cloudprovider/mocks"
|
||||
"github.com/heptio/velero/pkg/plugin/framework"
|
||||
)
|
||||
|
||||
func TestRestartableGetObjectStore(t *testing.T) {
|
||||
@@ -59,7 +60,7 @@ func TestRestartableGetObjectStore(t *testing.T) {
|
||||
defer p.AssertExpectations(t)
|
||||
|
||||
name := "aws"
|
||||
key := kindAndName{kind: PluginKindObjectStore, name: name}
|
||||
key := kindAndName{kind: framework.PluginKindObjectStore, name: name}
|
||||
p.On("getByKindAndName", key).Return(tc.plugin, tc.getError)
|
||||
|
||||
r := &restartableObjectStore{
|
||||
@@ -84,7 +85,7 @@ func TestRestartableObjectStoreReinitialize(t *testing.T) {
|
||||
defer p.AssertExpectations(t)
|
||||
|
||||
name := "aws"
|
||||
key := kindAndName{kind: PluginKindObjectStore, name: name}
|
||||
key := kindAndName{kind: framework.PluginKindObjectStore, name: name}
|
||||
r := &restartableObjectStore{
|
||||
key: key,
|
||||
sharedPluginProcess: p,
|
||||
@@ -117,7 +118,7 @@ func TestRestartableObjectStoreGetDelegate(t *testing.T) {
|
||||
// Reset error
|
||||
p.On("resetIfNeeded").Return(errors.Errorf("reset error")).Once()
|
||||
name := "aws"
|
||||
key := kindAndName{kind: PluginKindObjectStore, name: name}
|
||||
key := kindAndName{kind: framework.PluginKindObjectStore, name: name}
|
||||
r := &restartableObjectStore{
|
||||
key: key,
|
||||
sharedPluginProcess: p,
|
||||
@@ -145,7 +146,7 @@ func TestRestartableObjectStoreInit(t *testing.T) {
|
||||
|
||||
// getObjectStore error
|
||||
name := "aws"
|
||||
key := kindAndName{kind: PluginKindObjectStore, name: name}
|
||||
key := kindAndName{kind: framework.PluginKindObjectStore, name: name}
|
||||
r := &restartableObjectStore{
|
||||
key: key,
|
||||
sharedPluginProcess: p,
|
||||
@@ -185,7 +186,7 @@ func TestRestartableObjectStoreInit(t *testing.T) {
|
||||
func TestRestartableObjectStoreDelegatedFunctions(t *testing.T) {
|
||||
runRestartableDelegateTests(
|
||||
t,
|
||||
PluginKindObjectStore,
|
||||
framework.PluginKindObjectStore,
|
||||
func(key kindAndName, p RestartableProcess) interface{} {
|
||||
return &restartableObjectStore{
|
||||
key: key,
|
||||
|
||||
@@ -18,6 +18,7 @@ package plugin
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/heptio/velero/pkg/plugin/framework"
|
||||
"github.com/heptio/velero/pkg/plugin/velero"
|
||||
)
|
||||
|
||||
@@ -34,7 +35,7 @@ type restartableRestoreItemAction struct {
|
||||
// newRestartableRestoreItemAction returns a new restartableRestoreItemAction.
|
||||
func newRestartableRestoreItemAction(name string, sharedPluginProcess RestartableProcess) *restartableRestoreItemAction {
|
||||
r := &restartableRestoreItemAction{
|
||||
key: kindAndName{kind: PluginKindRestoreItemAction, name: name},
|
||||
key: kindAndName{kind: framework.PluginKindRestoreItemAction, name: name},
|
||||
sharedPluginProcess: sharedPluginProcess,
|
||||
}
|
||||
return r
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
v1 "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
"github.com/heptio/velero/pkg/plugin/framework"
|
||||
"github.com/heptio/velero/pkg/plugin/velero"
|
||||
"github.com/heptio/velero/pkg/restore/mocks"
|
||||
)
|
||||
@@ -58,7 +59,7 @@ func TestRestartableGetRestoreItemAction(t *testing.T) {
|
||||
defer p.AssertExpectations(t)
|
||||
|
||||
name := "pod"
|
||||
key := kindAndName{kind: PluginKindRestoreItemAction, name: name}
|
||||
key := kindAndName{kind: framework.PluginKindRestoreItemAction, name: name}
|
||||
p.On("getByKindAndName", key).Return(tc.plugin, tc.getError)
|
||||
|
||||
r := newRestartableRestoreItemAction(name, p)
|
||||
@@ -89,7 +90,7 @@ func TestRestartableRestoreItemActionGetDelegate(t *testing.T) {
|
||||
// Happy path
|
||||
p.On("resetIfNeeded").Return(nil)
|
||||
expected := new(mocks.ItemAction)
|
||||
key := kindAndName{kind: PluginKindRestoreItemAction, name: name}
|
||||
key := kindAndName{kind: framework.PluginKindRestoreItemAction, name: name}
|
||||
p.On("getByKindAndName", key).Return(expected, nil)
|
||||
|
||||
a, err = r.getDelegate()
|
||||
@@ -121,7 +122,7 @@ func TestRestartableRestoreItemActionDelegatedFunctions(t *testing.T) {
|
||||
|
||||
runRestartableDelegateTests(
|
||||
t,
|
||||
PluginKindRestoreItemAction,
|
||||
framework.PluginKindRestoreItemAction,
|
||||
func(key kindAndName, p RestartableProcess) interface{} {
|
||||
return &restartableRestoreItemAction{
|
||||
key: key,
|
||||
|
||||
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
Copyright 2017, 2019 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
proto "github.com/heptio/velero/pkg/plugin/generated"
|
||||
"github.com/heptio/velero/pkg/plugin/velero"
|
||||
)
|
||||
|
||||
// RestoreItemActionPlugin is an implementation of go-plugin's Plugin
|
||||
// interface with support for gRPC for the restore/ItemAction
|
||||
// interface.
|
||||
type RestoreItemActionPlugin struct {
|
||||
plugin.NetRPCUnsupportedPlugin
|
||||
*pluginBase
|
||||
}
|
||||
|
||||
var _ velero.RestoreItemAction = &RestoreItemActionGRPCClient{}
|
||||
|
||||
// NewRestoreItemActionPlugin constructs a RestoreItemActionPlugin.
|
||||
func NewRestoreItemActionPlugin(options ...pluginOption) *RestoreItemActionPlugin {
|
||||
return &RestoreItemActionPlugin{
|
||||
pluginBase: newPluginBase(options...),
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// client code
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// GRPCClient returns a RestoreItemAction gRPC client.
|
||||
func (p *RestoreItemActionPlugin) GRPCClient(c *grpc.ClientConn) (interface{}, error) {
|
||||
return newClientDispenser(p.clientLogger, c, newRestoreItemActionGRPCClient), nil
|
||||
}
|
||||
|
||||
// RestoreItemActionGRPCClient implements the backup/ItemAction interface and uses a
|
||||
// gRPC client to make calls to the plugin server.
|
||||
type RestoreItemActionGRPCClient struct {
|
||||
*clientBase
|
||||
grpcClient proto.RestoreItemActionClient
|
||||
}
|
||||
|
||||
func newRestoreItemActionGRPCClient(base *clientBase, clientConn *grpc.ClientConn) interface{} {
|
||||
return &RestoreItemActionGRPCClient{
|
||||
clientBase: base,
|
||||
grpcClient: proto.NewRestoreItemActionClient(clientConn),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RestoreItemActionGRPCClient) AppliesTo() (velero.ResourceSelector, error) {
|
||||
res, err := c.grpcClient.AppliesTo(context.Background(), &proto.AppliesToRequest{Plugin: c.plugin})
|
||||
if err != nil {
|
||||
return velero.ResourceSelector{}, err
|
||||
}
|
||||
|
||||
return velero.ResourceSelector{
|
||||
IncludedNamespaces: res.IncludedNamespaces,
|
||||
ExcludedNamespaces: res.ExcludedNamespaces,
|
||||
IncludedResources: res.IncludedResources,
|
||||
ExcludedResources: res.ExcludedResources,
|
||||
LabelSelector: res.Selector,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *RestoreItemActionGRPCClient) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) {
|
||||
itemJSON, err := json.Marshal(input.Item.UnstructuredContent())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
itemFromBackupJSON, err := json.Marshal(input.ItemFromBackup.UnstructuredContent())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
restoreJSON, err := json.Marshal(input.Restore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := &proto.RestoreExecuteRequest{
|
||||
Plugin: c.plugin,
|
||||
Item: itemJSON,
|
||||
ItemFromBackup: itemFromBackupJSON,
|
||||
Restore: restoreJSON,
|
||||
}
|
||||
|
||||
res, err := c.grpcClient.Execute(context.Background(), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var updatedItem unstructured.Unstructured
|
||||
if err := json.Unmarshal(res.Item, &updatedItem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var warning error
|
||||
if res.Warning != "" {
|
||||
warning = errors.New(res.Warning)
|
||||
}
|
||||
|
||||
return &velero.RestoreItemActionExecuteOutput{
|
||||
UpdatedItem: &updatedItem,
|
||||
Warning: warning,
|
||||
}, nil
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// server code
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// GRPCServer registers a RestoreItemAction gRPC server.
|
||||
func (p *RestoreItemActionPlugin) GRPCServer(s *grpc.Server) error {
|
||||
proto.RegisterRestoreItemActionServer(s, &RestoreItemActionGRPCServer{mux: p.serverMux})
|
||||
return nil
|
||||
}
|
||||
|
||||
// RestoreItemActionGRPCServer implements the proto-generated RestoreItemActionServer interface, and accepts
|
||||
// gRPC calls and forwards them to an implementation of the pluggable interface.
|
||||
type RestoreItemActionGRPCServer struct {
|
||||
mux *serverMux
|
||||
}
|
||||
|
||||
func (s *RestoreItemActionGRPCServer) getImpl(name string) (velero.RestoreItemAction, error) {
|
||||
impl, err := s.mux.getHandler(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
itemAction, ok := impl.(velero.RestoreItemAction)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("%T is not a restore item action", impl)
|
||||
}
|
||||
|
||||
return itemAction, nil
|
||||
}
|
||||
|
||||
func (s *RestoreItemActionGRPCServer) AppliesTo(ctx context.Context, req *proto.AppliesToRequest) (response *proto.AppliesToResponse, err error) {
|
||||
defer func() {
|
||||
if recoveredErr := handlePanic(recover()); recoveredErr != nil {
|
||||
err = recoveredErr
|
||||
}
|
||||
}()
|
||||
|
||||
impl, err := s.getImpl(req.Plugin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
appliesTo, err := impl.AppliesTo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &proto.AppliesToResponse{
|
||||
IncludedNamespaces: appliesTo.IncludedNamespaces,
|
||||
ExcludedNamespaces: appliesTo.ExcludedNamespaces,
|
||||
IncludedResources: appliesTo.IncludedResources,
|
||||
ExcludedResources: appliesTo.ExcludedResources,
|
||||
Selector: appliesTo.LabelSelector,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *RestoreItemActionGRPCServer) Execute(ctx context.Context, req *proto.RestoreExecuteRequest) (response *proto.RestoreExecuteResponse, err error) {
|
||||
defer func() {
|
||||
if recoveredErr := handlePanic(recover()); recoveredErr != nil {
|
||||
err = recoveredErr
|
||||
}
|
||||
}()
|
||||
|
||||
impl, err := s.getImpl(req.Plugin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
item unstructured.Unstructured
|
||||
itemFromBackup unstructured.Unstructured
|
||||
restoreObj api.Restore
|
||||
)
|
||||
|
||||
if err := json.Unmarshal(req.Item, &item); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(req.ItemFromBackup, &itemFromBackup); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(req.Restore, &restoreObj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
executeOutput, err := impl.Execute(&velero.RestoreItemActionExecuteInput{
|
||||
Item: &item,
|
||||
ItemFromBackup: &itemFromBackup,
|
||||
Restore: &restoreObj,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updatedItem, err := json.Marshal(executeOutput.UpdatedItem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var warnMessage string
|
||||
if executeOutput.Warning != nil {
|
||||
warnMessage = executeOutput.Warning.Error()
|
||||
}
|
||||
|
||||
return &proto.RestoreExecuteResponse{
|
||||
Item: updatedItem,
|
||||
Warning: warnMessage,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user