2021-09-23 08:33:54 -04:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
2023-02-02 17:15:17 -08:00
|
|
|
|
2023-04-19 11:27:00 -07:00
|
|
|
package loopback
|
2021-09-23 08:33:54 -04:00
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2021-09-28 16:19:32 -04:00
|
|
|
"fmt"
|
2021-09-23 08:33:54 -04:00
|
|
|
|
|
|
|
|
"github.com/hashicorp/boundary/internal/errors"
|
|
|
|
|
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
|
|
|
|
"github.com/mitchellh/mapstructure"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const loopbackPluginHostInfoAttrField = "host_info"
|
|
|
|
|
|
2023-04-19 11:27:00 -07:00
|
|
|
var (
|
|
|
|
|
_ plgpb.HostPluginServiceServer = (*LoopbackPlugin)(nil)
|
|
|
|
|
_ plgpb.HostPluginServiceServer = (*TestPluginServer)(nil)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// TestPluginServer provides a host plugin service server where each method can be overwritten for tests.
|
|
|
|
|
type TestPluginServer struct {
|
|
|
|
|
NormalizeCatalogDataFn func(context.Context, *plgpb.NormalizeCatalogDataRequest) (*plgpb.NormalizeCatalogDataResponse, error)
|
|
|
|
|
OnCreateCatalogFn func(context.Context, *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error)
|
|
|
|
|
OnUpdateCatalogFn func(context.Context, *plgpb.OnUpdateCatalogRequest) (*plgpb.OnUpdateCatalogResponse, error)
|
|
|
|
|
OnDeleteCatalogFn func(context.Context, *plgpb.OnDeleteCatalogRequest) (*plgpb.OnDeleteCatalogResponse, error)
|
|
|
|
|
NormalizeSetDataFn func(context.Context, *plgpb.NormalizeSetDataRequest) (*plgpb.NormalizeSetDataResponse, error)
|
|
|
|
|
OnCreateSetFn func(context.Context, *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error)
|
|
|
|
|
OnUpdateSetFn func(context.Context, *plgpb.OnUpdateSetRequest) (*plgpb.OnUpdateSetResponse, error)
|
|
|
|
|
OnDeleteSetFn func(context.Context, *plgpb.OnDeleteSetRequest) (*plgpb.OnDeleteSetResponse, error)
|
|
|
|
|
ListHostsFn func(context.Context, *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error)
|
|
|
|
|
plgpb.UnimplementedHostPluginServiceServer
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t TestPluginServer) NormalizeCatalogData(ctx context.Context, req *plgpb.NormalizeCatalogDataRequest) (*plgpb.NormalizeCatalogDataResponse, error) {
|
|
|
|
|
if t.NormalizeCatalogDataFn == nil {
|
|
|
|
|
return t.UnimplementedHostPluginServiceServer.NormalizeCatalogData(ctx, req)
|
|
|
|
|
}
|
|
|
|
|
return t.NormalizeCatalogDataFn(ctx, req)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t TestPluginServer) OnCreateCatalog(ctx context.Context, req *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) {
|
|
|
|
|
if t.OnCreateCatalogFn == nil {
|
|
|
|
|
return t.UnimplementedHostPluginServiceServer.OnCreateCatalog(ctx, req)
|
|
|
|
|
}
|
|
|
|
|
return t.OnCreateCatalogFn(ctx, req)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t TestPluginServer) OnUpdateCatalog(ctx context.Context, req *plgpb.OnUpdateCatalogRequest) (*plgpb.OnUpdateCatalogResponse, error) {
|
|
|
|
|
if t.OnUpdateCatalogFn == nil {
|
|
|
|
|
return t.UnimplementedHostPluginServiceServer.OnUpdateCatalog(ctx, req)
|
|
|
|
|
}
|
|
|
|
|
return t.OnUpdateCatalogFn(ctx, req)
|
|
|
|
|
}
|
2021-09-23 08:33:54 -04:00
|
|
|
|
2023-04-19 11:27:00 -07:00
|
|
|
func (t TestPluginServer) OnDeleteCatalog(ctx context.Context, req *plgpb.OnDeleteCatalogRequest) (*plgpb.OnDeleteCatalogResponse, error) {
|
|
|
|
|
if t.OnDeleteCatalogFn == nil {
|
|
|
|
|
return t.UnimplementedHostPluginServiceServer.OnDeleteCatalog(ctx, req)
|
|
|
|
|
}
|
|
|
|
|
return t.OnDeleteCatalogFn(ctx, req)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t TestPluginServer) NormalizeSetData(ctx context.Context, req *plgpb.NormalizeSetDataRequest) (*plgpb.NormalizeSetDataResponse, error) {
|
|
|
|
|
if t.NormalizeSetDataFn == nil {
|
|
|
|
|
return t.UnimplementedHostPluginServiceServer.NormalizeSetData(ctx, req)
|
|
|
|
|
}
|
|
|
|
|
return t.NormalizeSetDataFn(ctx, req)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t TestPluginServer) OnCreateSet(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) {
|
|
|
|
|
if t.OnCreateSetFn == nil {
|
|
|
|
|
return t.UnimplementedHostPluginServiceServer.OnCreateSet(ctx, req)
|
|
|
|
|
}
|
|
|
|
|
return t.OnCreateSetFn(ctx, req)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t TestPluginServer) OnUpdateSet(ctx context.Context, req *plgpb.OnUpdateSetRequest) (*plgpb.OnUpdateSetResponse, error) {
|
|
|
|
|
if t.OnUpdateSetFn == nil {
|
|
|
|
|
return t.UnimplementedHostPluginServiceServer.OnUpdateSet(ctx, req)
|
|
|
|
|
}
|
|
|
|
|
return t.OnUpdateSetFn(ctx, req)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t TestPluginServer) OnDeleteSet(ctx context.Context, req *plgpb.OnDeleteSetRequest) (*plgpb.OnDeleteSetResponse, error) {
|
|
|
|
|
if t.OnDeleteSetFn == nil {
|
|
|
|
|
return t.UnimplementedHostPluginServiceServer.OnDeleteSet(ctx, req)
|
|
|
|
|
}
|
|
|
|
|
return t.OnDeleteSetFn(ctx, req)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t TestPluginServer) ListHosts(ctx context.Context, req *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error) {
|
|
|
|
|
if t.ListHostsFn == nil {
|
|
|
|
|
return t.UnimplementedHostPluginServiceServer.ListHosts(ctx, req)
|
|
|
|
|
}
|
|
|
|
|
return t.ListHostsFn(ctx, req)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LoopbackPlugin provides a host plugin with functionality useful for certain
|
2021-09-23 08:33:54 -04:00
|
|
|
// kinds of testing. It is not (currently) thread-safe.
|
|
|
|
|
//
|
|
|
|
|
// Over time, if useful, it can be enhanced to do things like handle multiple
|
|
|
|
|
// hosts per set.
|
2023-04-19 11:27:00 -07:00
|
|
|
type LoopbackPlugin struct {
|
2021-09-23 08:33:54 -04:00
|
|
|
*TestPluginServer
|
|
|
|
|
|
2021-09-28 16:19:32 -04:00
|
|
|
hostMap map[string][]*loopbackPluginHostInfo
|
2021-09-23 08:33:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type loopbackPluginHostInfo struct {
|
|
|
|
|
ExternalId string `mapstructure:"external_id"`
|
|
|
|
|
IpAddresses []string `mapstructure:"ip_addresses"`
|
|
|
|
|
DnsNames []string `mapstructure:"dns_names"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewLoopbackPlugin returns a new loopback plugin
|
2023-04-19 11:27:00 -07:00
|
|
|
func NewLoopbackPlugin() (*LoopbackPlugin, error) {
|
|
|
|
|
ret := &LoopbackPlugin{
|
2021-09-23 08:33:54 -04:00
|
|
|
TestPluginServer: new(TestPluginServer),
|
2021-09-28 16:19:32 -04:00
|
|
|
hostMap: make(map[string][]*loopbackPluginHostInfo),
|
2021-09-23 08:33:54 -04:00
|
|
|
}
|
|
|
|
|
ret.OnCreateCatalogFn = ret.onCreateCatalog
|
2021-11-12 09:46:40 -07:00
|
|
|
ret.OnUpdateCatalogFn = ret.onUpdateCatalog
|
2021-11-13 17:18:20 -05:00
|
|
|
ret.OnCreateSetFn = ret.onCreateSet
|
|
|
|
|
ret.OnUpdateSetFn = ret.onUpdateSet
|
2021-09-23 08:33:54 -04:00
|
|
|
ret.OnDeleteSetFn = ret.onDeleteSet
|
|
|
|
|
ret.ListHostsFn = ret.listHosts
|
2023-04-19 11:27:00 -07:00
|
|
|
|
|
|
|
|
return ret, nil
|
2021-09-23 08:33:54 -04:00
|
|
|
}
|
|
|
|
|
|
2023-04-19 11:27:00 -07:00
|
|
|
func (l *LoopbackPlugin) onCreateCatalog(ctx context.Context, req *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) {
|
|
|
|
|
const op = "loopback.(loopbackPlugin).onCreateCatalog"
|
2021-09-23 08:33:54 -04:00
|
|
|
if req == nil {
|
|
|
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "req is nil")
|
|
|
|
|
}
|
|
|
|
|
if cat := req.GetCatalog(); cat != nil {
|
|
|
|
|
if secrets := cat.GetSecrets(); secrets != nil {
|
|
|
|
|
return &plgpb.OnCreateCatalogResponse{
|
|
|
|
|
Persisted: &plgpb.HostCatalogPersisted{
|
|
|
|
|
Secrets: secrets,
|
|
|
|
|
},
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-11-12 09:46:40 -07:00
|
|
|
return &plgpb.OnCreateCatalogResponse{}, nil
|
2021-09-23 08:33:54 -04:00
|
|
|
}
|
|
|
|
|
|
2023-04-19 11:27:00 -07:00
|
|
|
func (l *LoopbackPlugin) onUpdateCatalog(ctx context.Context, req *plgpb.OnUpdateCatalogRequest) (*plgpb.OnUpdateCatalogResponse, error) {
|
|
|
|
|
const op = "loopback.(loopbackPlugin).onUpdateCatalog"
|
2021-11-13 17:18:20 -05:00
|
|
|
if req == nil {
|
|
|
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "req is nil")
|
|
|
|
|
}
|
|
|
|
|
if cat := req.GetNewCatalog(); cat != nil {
|
|
|
|
|
if secrets := cat.GetSecrets(); secrets != nil {
|
|
|
|
|
return &plgpb.OnUpdateCatalogResponse{
|
|
|
|
|
Persisted: &plgpb.HostCatalogPersisted{
|
|
|
|
|
Secrets: secrets,
|
|
|
|
|
},
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return &plgpb.OnUpdateCatalogResponse{}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-19 11:27:00 -07:00
|
|
|
func (l *LoopbackPlugin) onCreateSet(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) {
|
|
|
|
|
const op = "loopback.(loopbackPlugin).onCreateSet"
|
2021-09-23 08:33:54 -04:00
|
|
|
if req == nil {
|
|
|
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "req is nil")
|
|
|
|
|
}
|
|
|
|
|
set := req.GetSet()
|
|
|
|
|
if set == nil {
|
|
|
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "set is nil")
|
|
|
|
|
}
|
|
|
|
|
if attrs := set.GetAttributes(); attrs != nil {
|
|
|
|
|
attrsMap := attrs.AsMap()
|
|
|
|
|
if field := attrsMap[loopbackPluginHostInfoAttrField]; field != nil {
|
2021-09-28 16:19:32 -04:00
|
|
|
switch t := field.(type) {
|
2022-10-25 18:29:38 -05:00
|
|
|
case []any:
|
2021-09-28 16:19:32 -04:00
|
|
|
for _, h := range t {
|
|
|
|
|
hostInfo := new(loopbackPluginHostInfo)
|
|
|
|
|
if err := mapstructure.Decode(h, hostInfo); err != nil {
|
|
|
|
|
return nil, errors.Wrap(ctx, err, op)
|
|
|
|
|
}
|
|
|
|
|
l.hostMap[set.GetId()] = append(l.hostMap[set.GetId()], hostInfo)
|
|
|
|
|
}
|
2022-10-25 18:29:38 -05:00
|
|
|
case map[string]any:
|
2021-09-28 16:19:32 -04:00
|
|
|
hostInfo := new(loopbackPluginHostInfo)
|
|
|
|
|
if err := mapstructure.Decode(t, hostInfo); err != nil {
|
|
|
|
|
return nil, errors.Wrap(ctx, err, op)
|
|
|
|
|
}
|
|
|
|
|
l.hostMap[set.GetId()] = append(l.hostMap[set.GetId()], hostInfo)
|
|
|
|
|
default:
|
|
|
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unknown host info type %T", t))
|
2021-09-23 08:33:54 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-11-13 17:18:20 -05:00
|
|
|
return &plgpb.OnCreateSetResponse{}, nil
|
2021-09-23 08:33:54 -04:00
|
|
|
}
|
|
|
|
|
|
2023-04-19 11:27:00 -07:00
|
|
|
func (l *LoopbackPlugin) onUpdateSet(ctx context.Context, req *plgpb.OnUpdateSetRequest) (*plgpb.OnUpdateSetResponse, error) {
|
|
|
|
|
const op = "loopback.(loopbackPlugin).onCreateSet"
|
2021-11-12 09:46:40 -07:00
|
|
|
if req == nil {
|
|
|
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "req is nil")
|
|
|
|
|
}
|
2021-11-13 17:18:20 -05:00
|
|
|
set := req.GetNewSet()
|
|
|
|
|
if set == nil {
|
|
|
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "set is nil")
|
|
|
|
|
}
|
|
|
|
|
var hosts []*loopbackPluginHostInfo
|
|
|
|
|
if attrs := set.GetAttributes(); attrs != nil {
|
|
|
|
|
attrsMap := attrs.AsMap()
|
|
|
|
|
if field := attrsMap[loopbackPluginHostInfoAttrField]; field != nil {
|
|
|
|
|
switch t := field.(type) {
|
2022-10-25 18:29:38 -05:00
|
|
|
case []any:
|
2021-11-13 17:18:20 -05:00
|
|
|
for _, h := range t {
|
|
|
|
|
hostInfo := new(loopbackPluginHostInfo)
|
|
|
|
|
if err := mapstructure.Decode(h, hostInfo); err != nil {
|
|
|
|
|
return nil, errors.Wrap(ctx, err, op)
|
|
|
|
|
}
|
|
|
|
|
hosts = append(hosts, hostInfo)
|
|
|
|
|
}
|
2022-10-25 18:29:38 -05:00
|
|
|
case map[string]any:
|
2021-11-13 17:18:20 -05:00
|
|
|
hostInfo := new(loopbackPluginHostInfo)
|
|
|
|
|
if err := mapstructure.Decode(t, hostInfo); err != nil {
|
|
|
|
|
return nil, errors.Wrap(ctx, err, op)
|
|
|
|
|
}
|
|
|
|
|
hosts = append(hosts, hostInfo)
|
|
|
|
|
default:
|
|
|
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unknown host info type %T", t))
|
|
|
|
|
}
|
2021-11-12 09:46:40 -07:00
|
|
|
}
|
|
|
|
|
}
|
2021-11-13 17:18:20 -05:00
|
|
|
if hosts != nil {
|
|
|
|
|
l.hostMap[set.GetId()] = hosts
|
|
|
|
|
}
|
|
|
|
|
return &plgpb.OnUpdateSetResponse{}, nil
|
2021-11-12 09:46:40 -07:00
|
|
|
}
|
|
|
|
|
|
2023-04-19 11:27:00 -07:00
|
|
|
func (l *LoopbackPlugin) onDeleteSet(ctx context.Context, req *plgpb.OnDeleteSetRequest) (*plgpb.OnDeleteSetResponse, error) {
|
|
|
|
|
const op = "loopback.(loopbackPlugin).onDeleteSet"
|
2021-09-23 08:33:54 -04:00
|
|
|
if req == nil {
|
|
|
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "req is nil")
|
|
|
|
|
}
|
|
|
|
|
set := req.GetSet()
|
|
|
|
|
if set == nil {
|
|
|
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "set is nil")
|
|
|
|
|
}
|
|
|
|
|
delete(l.hostMap, set.GetId())
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-19 11:27:00 -07:00
|
|
|
func (l *LoopbackPlugin) listHosts(ctx context.Context, req *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error) {
|
|
|
|
|
const op = "loopback.(loopbackPlugin).listHosts"
|
2021-09-23 08:33:54 -04:00
|
|
|
if req == nil {
|
|
|
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "req is nil")
|
|
|
|
|
}
|
|
|
|
|
resp := new(plgpb.ListHostsResponse)
|
|
|
|
|
for _, set := range req.GetSets() {
|
2021-09-28 16:19:32 -04:00
|
|
|
hostInfos := l.hostMap[set.GetId()]
|
|
|
|
|
if len(hostInfos) == 0 {
|
2021-09-23 08:33:54 -04:00
|
|
|
continue
|
|
|
|
|
}
|
2021-09-28 16:19:32 -04:00
|
|
|
for _, host := range hostInfos {
|
|
|
|
|
resp.Hosts = append(resp.Hosts, &plgpb.ListHostsResponseHost{
|
|
|
|
|
SetIds: []string{set.GetId()},
|
|
|
|
|
ExternalId: host.ExternalId,
|
|
|
|
|
IpAddresses: host.IpAddresses,
|
|
|
|
|
DnsNames: host.DnsNames,
|
|
|
|
|
})
|
|
|
|
|
}
|
2021-09-23 08:33:54 -04:00
|
|
|
}
|
|
|
|
|
return resp, nil
|
|
|
|
|
}
|