Placeholder Plugin Providers for Repos and added testPlugin struct (#1532)

* Create minimalistic plugin map and test plugin.

* Pass secrets and attributes to the plugin when creating catalogs or sets.  Add tests for CreateSet and CreateCatalog repo methods.

* make fmt and extract out getPrivateCatalog and getCatalog helper methods.

* Exporting TestPluginServer so it can be used in the handlers.

* Keep separate persisted data from host catalogs lookups which facilitate passing it onto the plugins.

* Add TODO to merge get Catalog and get Persisted data.

* Adding tests for unimplemented being returned by the plugin.

* Use GetFields() instead of AsMap() to confirm attributes should be populated. Make clone consistent between catalog and sets.

Co-authored-by: Jeff Mitchell <jeffrey.mitchell@gmail.com>
This commit is contained in:
ToddandJeff Mitchell authored and GitHub committed 2021-09-21 09:18:49 -07:00
1 parent ea939a4445
commit bbfef2802f
30 files changed
+669 -227

No files matched your search

@@ -54,9 +54,7 @@ begin;
create_time wt_timestamp,
update_time wt_timestamp,
version wt_version,
attributes bytea not null
constraint attributes_must_not_be_empty
check(length(attributes) > 0),
attributes bytea not null,
constraint host_catalog_fkey
foreign key (scope_id, public_id)
references host_catalog (scope_id, public_id)
@@ -126,9 +124,7 @@ begin;
create_time wt_timestamp,
update_time wt_timestamp,
version wt_version,
attributes bytea not null
constraint attributes_must_not_be_empty
check(length(attributes) > 0),
attributes bytea not null,
constraint host_plugin_set_catalog_id_name_uq
unique(catalog_id, name),
constraint host_set_fkey
+3 -7
View File
@@ -7742,9 +7742,7 @@ alter table wh_session_connection_accumulating_fact
create_time wt_timestamp,
update_time wt_timestamp,
version wt_version,
attributes bytea not null
constraint attributes_must_not_be_empty
check(length(attributes) > 0),
attributes bytea not null,
constraint host_catalog_fkey
foreign key (scope_id, public_id)
references host_catalog (scope_id, public_id)
@@ -7814,9 +7812,7 @@ alter table wh_session_connection_accumulating_fact
create_time wt_timestamp,
update_time wt_timestamp,
version wt_version,
attributes bytea not null
constraint attributes_must_not_be_empty
check(length(attributes) > 0),
attributes bytea not null,
constraint host_plugin_set_catalog_id_name_uq
unique(catalog_id, name),
constraint host_set_fkey
@@ -7864,7 +7860,7 @@ create table host_set_preferred_endpoint (
constraint priority_must_be_greater_than_zero
check(priority > 0),
condition text not null
constraint condition_must_not_be_empty
constraint condition_must_not_be_too_short
check(length(trim(condition)) > 4) -- minimum is 'dns:*'
constraint condition_must_not_be_too_long
check(length(trim(condition)) < 255)
+15 -16
View File
@@ -5,12 +5,12 @@ package plugin
import (
"context"
"encoding/json"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/host/plugin/store"
"github.com/hashicorp/boundary/internal/oplog"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
)
// A HostCatalog contains plugin host sets. It is owned by
@@ -19,7 +19,7 @@ type HostCatalog struct {
*store.HostCatalog
tableName string `gorm:"-"`
secrets map[string]interface{} `gorm:"-"`
secrets *structpb.Struct `gorm:"-"`
}
// NewHostCatalog creates a new in memory HostCatalog assigned to a scopeId
@@ -28,23 +28,22 @@ type HostCatalog struct {
func NewHostCatalog(ctx context.Context, scopeId, pluginId string, opt ...Option) (*HostCatalog, error) {
const op = "plugin.NewHostCatalog"
opts := getOpts(opt...)
attrs, err := proto.Marshal(opts.withAttributes)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter))
}
hc := &HostCatalog{
HostCatalog: &store.HostCatalog{
ScopeId: scopeId,
PluginId: pluginId,
Name: opts.withName,
Description: opts.withDescription,
Attributes: attrs,
},
secrets: opts.withSecrets,
}
if opts.withAttributes != nil {
attrs, err := json.Marshal(opts.withAttributes)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter))
}
hc.Attributes = attrs
}
return hc, nil
}
@@ -58,16 +57,16 @@ func allocHostCatalog() *HostCatalog {
// secret. The secret shallow copied.
func (c *HostCatalog) clone() *HostCatalog {
cp := proto.Clone(c.HostCatalog)
newSecret := proto.Clone(c.secrets)
hc := &HostCatalog{
HostCatalog: cp.(*store.HostCatalog),
secrets: newSecret.(*structpb.Struct),
}
if c.secrets != nil {
newSecret := make(map[string]interface{}, len(c.secrets))
for k, v := range c.secrets {
newSecret[k] = v
}
hc.secrets = newSecret
// proto.Clone will convert slices with length and capacity of 0 to nil.
// Fix this since gorm treats empty slices differently than nil.
if c.Attributes != nil && len(c.Attributes) == 0 && hc.Attributes == nil {
hc.Attributes = []byte{}
}
return hc
}
+3 -3
View File
@@ -2,7 +2,6 @@ package plugin
import (
"context"
"encoding/json"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
@@ -11,6 +10,7 @@ import (
wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/hashicorp/go-kms-wrapping/structwrapping"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
)
// HostCatalogSecret contains the encrypted secret for a host catalog.
@@ -22,7 +22,7 @@ type HostCatalogSecret struct {
// newHostCatalogSecret creates an in memory host catalog secret.
// All options are ignored.
func newHostCatalogSecret(ctx context.Context, catalogId string, secret map[string]interface{}, _ ...Option) (*HostCatalogSecret, error) {
func newHostCatalogSecret(ctx context.Context, catalogId string, secret *structpb.Struct, _ ...Option) (*HostCatalogSecret, error) {
const op = "plugin.newHostCatlogSecret"
hcs := &HostCatalogSecret{
HostCatalogSecret: &store.HostCatalogSecret{
@@ -31,7 +31,7 @@ func newHostCatalogSecret(ctx context.Context, catalogId string, secret map[stri
}
if secret != nil {
attrs, err := json.Marshal(secret)
attrs, err := proto.Marshal(secret)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter))
}
@@ -2,7 +2,6 @@ package plugin
import (
"context"
"encoding/json"
"testing"
"github.com/google/go-cmp/cmp"
@@ -14,7 +13,9 @@ import (
"github.com/hashicorp/boundary/internal/plugin/host"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
"google.golang.org/protobuf/types/known/structpb"
)
func TestHostCatalogSecret_New(t *testing.T) {
@@ -28,7 +29,7 @@ func TestHostCatalogSecret_New(t *testing.T) {
type args struct {
catalogId string
attrs map[string]interface{}
attrs *structpb.Struct
}
tests := []struct {
@@ -64,13 +65,19 @@ func TestHostCatalogSecret_New(t *testing.T) {
name: "valid",
args: args{
catalogId: cat.GetPublicId(),
attrs: map[string]interface{}{"foo": "bar"},
attrs: func() *structpb.Struct {
st, err := structpb.NewStruct(map[string]interface{}{"foo": "bar"})
require.NoError(t, err)
return st
}(),
},
want: &HostCatalogSecret{
HostCatalogSecret: &store.HostCatalogSecret{
CatalogId: cat.GetPublicId(),
Secret: func() []byte {
b, err := json.Marshal(map[string]interface{}{"foo": "bar"})
st, err := structpb.NewStruct(map[string]interface{}{"foo": "bar"})
require.NoError(t, err)
b, err := proto.Marshal(st)
require.NoError(t, err)
return b
}(),
@@ -124,7 +131,8 @@ func TestHostCatalogSecret_Custom_Queries(t *testing.T) {
databaseWrapper, err := kkms.GetWrapper(ctx, prj.PublicId, kms.KeyPurposeDatabase)
require.NoError(t, err)
hcs, err := newHostCatalogSecret(ctx, cat.GetPublicId(), map[string]interface{}{"foo": "bar"})
hcs, err := newHostCatalogSecret(ctx, cat.GetPublicId(),
&structpb.Struct{Fields: map[string]*structpb.Value{"foo": structpb.NewStringValue("bar")}})
require.NoError(t, err)
assert.NoError(t, hcs.encrypt(ctx, databaseWrapper))
q, v := hcs.insertQuery()
@@ -141,7 +149,8 @@ func TestHostCatalogSecret_Custom_Queries(t *testing.T) {
assert.Empty(t, cmp.Diff(hcs, found, protocmp.Transform()))
// Update the secret and see the value updated.
updated, err := newHostCatalogSecret(ctx, cat.GetPublicId(), map[string]interface{}{"updated": "value"})
updated, err := newHostCatalogSecret(ctx, cat.GetPublicId(),
&structpb.Struct{Fields: map[string]*structpb.Value{"updated": structpb.NewStringValue("value")}})
require.NoError(t, err)
assert.NoError(t, updated.encrypt(ctx, databaseWrapper))
q, v = updated.insertQuery()
+9 -8
View File
@@ -2,7 +2,6 @@ package plugin
import (
"context"
"encoding/json"
"testing"
"github.com/google/go-cmp/cmp"
@@ -13,7 +12,9 @@ import (
"github.com/hashicorp/boundary/internal/plugin/host"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
"google.golang.org/protobuf/types/known/structpb"
)
func TestHostCatalog_Create(t *testing.T) {
@@ -45,7 +46,7 @@ func TestHostCatalog_Create(t *testing.T) {
want: &HostCatalog{
HostCatalog: &store.HostCatalog{
PluginId: plg.GetPublicId(),
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
wantCreateErr: true,
@@ -59,7 +60,7 @@ func TestHostCatalog_Create(t *testing.T) {
want: &HostCatalog{
HostCatalog: &store.HostCatalog{
ScopeId: prj.GetPublicId(),
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
wantCreateErr: true,
@@ -74,7 +75,7 @@ func TestHostCatalog_Create(t *testing.T) {
HostCatalog: &store.HostCatalog{
PluginId: plg.GetPublicId(),
ScopeId: prj.GetPublicId(),
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
},
@@ -92,7 +93,7 @@ func TestHostCatalog_Create(t *testing.T) {
PluginId: plg.GetPublicId(),
ScopeId: prj.GetPublicId(),
Name: "test-name",
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
},
@@ -110,7 +111,7 @@ func TestHostCatalog_Create(t *testing.T) {
PluginId: plg.GetPublicId(),
ScopeId: prj.GetPublicId(),
Description: "test-description",
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
},
@@ -120,7 +121,7 @@ func TestHostCatalog_Create(t *testing.T) {
pluginId: plg.GetPublicId(),
scopeId: prj.GetPublicId(),
opts: []Option{
WithAttributes(map[string]interface{}{"foo": "bar"}),
WithAttributes(&structpb.Struct{Fields: map[string]*structpb.Value{"foo": structpb.NewStringValue("bar")}}),
},
},
want: func() *HostCatalog {
@@ -131,7 +132,7 @@ func TestHostCatalog_Create(t *testing.T) {
},
}
var err error
hc.Attributes, err = json.Marshal(map[string]interface{}{"foo": "bar"})
hc.Attributes, err = proto.Marshal(&structpb.Struct{Fields: map[string]*structpb.Value{"foo": structpb.NewStringValue("bar")}})
require.NoError(t, err)
return hc
}(),
+11 -10
View File
@@ -2,7 +2,6 @@ package plugin
import (
"context"
"encoding/json"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/host/plugin/store"
@@ -22,23 +21,21 @@ type HostSet struct {
func NewHostSet(ctx context.Context, catalogId string, opt ...Option) (*HostSet, error) {
const op = "plugin.NewHostSet"
opts := getOpts(opt...)
attrs, err := proto.Marshal(opts.withAttributes)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter))
}
set := &HostSet{
HostSet: &store.HostSet{
CatalogId: catalogId,
Name: opts.withName,
Description: opts.withDescription,
Attributes: attrs,
PreferredEndpoints: opts.withPreferredEndpoints,
},
}
if opts.withAttributes != nil {
attrs, err := json.Marshal(opts.withAttributes)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter))
}
set.Attributes = attrs
}
return set, nil
}
@@ -64,9 +61,13 @@ func allocHostSet() *HostSet {
func (s *HostSet) clone() *HostSet {
cp := proto.Clone(s.HostSet)
return &HostSet{
hs := &HostSet{
HostSet: cp.(*store.HostSet),
}
if s.Attributes != nil && len(s.Attributes) == 0 && hs.Attributes == nil {
hs.Attributes = []byte{}
}
return hs
}
func (s *HostSet) oplog(op oplog.OpType) oplog.Metadata {
+17 -10
View File
@@ -10,6 +10,8 @@ import (
"github.com/hashicorp/boundary/internal/plugin/host"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
)
func TestHostSet_Create(t *testing.T) {
@@ -37,7 +39,7 @@ func TestHostSet_Create(t *testing.T) {
catalogId: "",
},
want: &HostSet{HostSet: &store.HostSet{
Attributes: []byte("{}"),
Attributes: []byte{},
}},
wantErr: true,
},
@@ -49,7 +51,7 @@ func TestHostSet_Create(t *testing.T) {
want: &HostSet{
HostSet: &store.HostSet{
CatalogId: cat.GetPublicId(),
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
},
@@ -65,7 +67,7 @@ func TestHostSet_Create(t *testing.T) {
HostSet: &store.HostSet{
CatalogId: cat.GetPublicId(),
Name: "test-name",
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
},
@@ -81,7 +83,7 @@ func TestHostSet_Create(t *testing.T) {
HostSet: &store.HostSet{
CatalogId: cat.GetPublicId(),
Name: "test-name",
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
wantErr: true,
@@ -98,7 +100,7 @@ func TestHostSet_Create(t *testing.T) {
HostSet: &store.HostSet{
CatalogId: cat2.GetPublicId(),
Name: "test-name",
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
},
@@ -114,7 +116,7 @@ func TestHostSet_Create(t *testing.T) {
HostSet: &store.HostSet{
CatalogId: cat.GetPublicId(),
Description: "test-description",
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
},
@@ -123,13 +125,18 @@ func TestHostSet_Create(t *testing.T) {
args: args{
catalogId: cat.GetPublicId(),
opts: []Option{
WithAttributes(map[string]interface{}{"foo": "bar"}),
WithAttributes(&structpb.Struct{Fields: map[string]*structpb.Value{"foo": structpb.NewStringValue("bar")}}),
},
},
want: &HostSet{
HostSet: &store.HostSet{
CatalogId: cat.GetPublicId(),
Attributes: []byte(`{"foo":"bar"}`),
CatalogId: cat.GetPublicId(),
Attributes: func() []byte {
st := &structpb.Struct{Fields: map[string]*structpb.Value{"foo": structpb.NewStringValue("bar")}}
b, err := proto.Marshal(st)
require.NoError(t, err)
return b
}(),
},
},
},
@@ -145,7 +152,7 @@ func TestHostSet_Create(t *testing.T) {
HostSet: &store.HostSet{
CatalogId: cat.GetPublicId(),
PreferredEndpoints: []string{"cidr:1.2.3.4"},
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
},
@@ -10,6 +10,7 @@ import (
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/plugin/host"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
@@ -101,7 +102,9 @@ func TestPluginSet_ImmutableFields(t *testing.T) {
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
plg := host.TestPlugin(t, conn, "test", "prefix")
cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
new := TestSet(t, conn, kmsCache, cat)
new := TestSet(t, conn, kmsCache, cat, map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &TestPluginServer{},
})
tests := []struct {
name string
+7 -7
View File
@@ -1,5 +1,7 @@
package plugin
import "google.golang.org/protobuf/types/known/structpb"
// getOpts - iterate the inbound Options and return a struct
func getOpts(opt ...Option) options {
opts := getDefaultOptions()
@@ -16,16 +18,14 @@ type Option func(*options)
type options struct {
withName string
withDescription string
withAttributes map[string]interface{}
withSecrets map[string]interface{}
withAttributes *structpb.Struct
withSecrets *structpb.Struct
withPreferredEndpoints []string
}
func getDefaultOptions() options {
return options{
withDescription: "",
withName: "",
withAttributes: make(map[string]interface{}),
withAttributes: &structpb.Struct{},
}
}
@@ -44,14 +44,14 @@ func WithName(name string) Option {
}
// WithAttributes provides an optional attributes field.
func WithAttributes(attrs map[string]interface{}) Option {
func WithAttributes(attrs *structpb.Struct) Option {
return func(o *options) {
o.withAttributes = attrs
}
}
// WithSecrets provides an optional secrets field.
func WithSecrets(secrets map[string]interface{}) Option {
func WithSecrets(secrets *structpb.Struct) Option {
return func(o *options) {
o.withSecrets = secrets
}
+15 -1
View File
@@ -8,6 +8,7 @@ import (
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/host"
"github.com/hashicorp/boundary/internal/kms"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
)
// A Repository stores and retrieves the persistent types in the plugin
@@ -16,6 +17,11 @@ type Repository struct {
reader db.Reader
writer db.Writer
kms *kms.Kms
// plugins is a map from plugin resource id to host plugin client.
// TODO: When we are using go-plugin change from using a plgpb.HostPluginServiceServer
// to the client.
plugins map[string]plgpb.HostPluginServiceServer
// defaultLimit provides a default for limiting the number of results
// returned from the repo
defaultLimit int
@@ -25,7 +31,7 @@ type Repository struct {
// only be used for one transaction and it is not safe for concurrent go
// routines to access it. WithLimit option is used as a repo wide default
// limit applied to all ListX methods.
func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...host.Option) (*Repository, error) {
func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, plgm map[string]plgpb.HostPluginServiceServer, opt ...host.Option) (*Repository, error) {
const op = "plugin.NewRepository"
switch {
case r == nil:
@@ -34,6 +40,8 @@ func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...host.Option) (
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "db.Writer")
case kms == nil:
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "kms")
case plgm == nil:
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "plgm")
}
opts, err := host.GetOpts(opt...)
@@ -45,10 +53,16 @@ func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...host.Option) (
opts.WithLimit = db.DefaultLimit
}
plgs := make(map[string]plgpb.HostPluginServiceServer, len(plgm))
for k, v := range plgm {
plgs[k] = v
}
return &Repository{
reader: r,
writer: w,
kms: kms,
plugins: plgs,
defaultLimit: opts.WithLimit,
}, nil
}
+102 -12
View File
@@ -10,6 +10,12 @@ import (
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
hostplg "github.com/hashicorp/boundary/internal/plugin/host"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
)
// CreateCatalog inserts c into the repository and returns a new
@@ -44,7 +50,6 @@ func (r *Repository) CreateCatalog(ctx context.Context, c *HostCatalog, _ ...Opt
}
c = c.clone()
// TODO: Capture this in a plugin manager and call the plugin's OnCreateCatalog function
plg := hostplg.NewPlugin("", "")
plg.PublicId = c.PluginId
if err := r.reader.LookupByPublicId(ctx, plg); err != nil {
@@ -60,27 +65,41 @@ func (r *Repository) CreateCatalog(ctx context.Context, c *HostCatalog, _ ...Opt
}
c.PublicId = id
oplogWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeOplog)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
plgClient, ok := r.plugins[plg.GetPublicId()]
if !ok || plgClient == nil {
return nil, errors.New(ctx, errors.Internal, op, fmt.Sprintf("expected plugin %q not available", plg.GetPluginName()))
}
dbWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeDatabase)
plgHc, err := toPluginCatalog(ctx, c)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get db wrapper"))
return nil, errors.Wrap(ctx, err, op)
}
plgResp, err := plgClient.OnCreateCatalog(ctx, &plgpb.OnCreateCatalogRequest{Catalog: plgHc})
if err != nil {
if status.Code(err) != codes.Unimplemented {
return nil, errors.Wrap(ctx, err, op)
}
}
var hcSecret *HostCatalogSecret
if c.secrets != nil {
// TODO: Create the secret using the returned value from the call to the plugin.
hcSecret, err = newHostCatalogSecret(ctx, id, c.secrets)
if plgResp != nil && plgResp.GetPersisted().GetData() != nil {
hcSecret, err = newHostCatalogSecret(ctx, id, plgResp.GetPersisted().GetData())
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
dbWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeDatabase)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get db wrapper"))
}
if err := hcSecret.encrypt(ctx, dbWrapper); err != nil {
return nil, errors.Wrap(ctx, err, op)
}
}
oplogWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeOplog)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
}
var newHostCatalog *HostCatalog
_, err = r.writer.DoTx(
ctx,
@@ -137,9 +156,8 @@ func (r *Repository) LookupCatalog(ctx context.Context, id string, _ ...Option)
if id == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id")
}
c := allocHostCatalog()
c.PublicId = id
if err := r.reader.LookupByPublicId(ctx, c); err != nil {
c, err := r.getCatalog(ctx, id)
if err != nil {
if errors.IsNotFoundError(err) {
return nil, nil
}
@@ -169,3 +187,75 @@ func (r *Repository) ListCatalogs(ctx context.Context, scopeIds []string, opt ..
}
return hostCatalogs, nil
}
// getCatalog retrieves the *HostCatalog with the provided id. If it is not found or there
// is an problem getting it from the database an error is returned instead.
func (r *Repository) getCatalog(ctx context.Context, id string) (*HostCatalog, error) {
const op = "plugin.(Repository).getCatalog"
c := allocHostCatalog()
c.PublicId = id
if err := r.reader.LookupByPublicId(ctx, c); err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", id)))
}
return c, nil
}
// getPersistedDataForCatalog returns the persisted data for a catalog if
// present. c must have a valid Public Id and Scope Id set.
// TODO: consider merging the functions for getting catalog and persisted data into a view.
func (r *Repository) getPersistedDataForCatalog(ctx context.Context, c *HostCatalog) (*plgpb.HostCatalogPersisted, error) {
const op = "plugin.(Repository).getPersistedDataForCatalog"
if c.PublicId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "empty public id")
}
if c.ScopeId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "empty scope id")
}
cSecret := allocHostCatalogSecret()
if err := r.reader.LookupWhere(ctx, cSecret, "catalog_id=?", c.GetPublicId()); err != nil {
if errors.IsNotFoundError(err) {
return nil, nil
}
return nil, errors.Wrap(ctx, err, op)
}
if cSecret == nil {
return nil, nil
}
dbWrapper, err := r.kms.GetWrapper(ctx, c.ScopeId, kms.KeyPurposeDatabase)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get db wrapper"))
}
if err := cSecret.decrypt(ctx, dbWrapper); err != nil {
return nil, errors.Wrap(ctx, err, op)
}
data := &structpb.Struct{}
if err := proto.Unmarshal(cSecret.GetSecret(), data); err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unmarshaling secret"))
}
return &plgpb.HostCatalogPersisted{Data: data}, nil
}
// toPluginCatalog returns a host catalog, with it's secret if available, in the format expected
// by the host plugin system.
func toPluginCatalog(ctx context.Context, in *HostCatalog) (*pb.HostCatalog, error) {
const op = "plugin.toPluginCatalog"
if in == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil storage plugin")
}
hc := &pb.HostCatalog{
Id: in.GetPublicId(),
ScopeId: in.GetScopeId(),
}
if in.GetAttributes() != nil {
attrs := &structpb.Struct{}
if err := proto.Unmarshal(in.GetAttributes(), attrs); err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to unmarshal attributes"))
}
hc.Attributes = attrs
}
if in.secrets != nil {
hc.Secrets = in.secrets
}
return hc, nil
}
@@ -6,6 +6,7 @@ import (
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/host/plugin/store"
@@ -13,8 +14,12 @@ import (
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
hostplg "github.com/hashicorp/boundary/internal/plugin/host"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
"google.golang.org/protobuf/types/known/structpb"
)
func TestRepository_CreateCatalog(t *testing.T) {
@@ -24,13 +29,27 @@ func TestRepository_CreateCatalog(t *testing.T) {
wrapper := db.TestWrapper(t)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
plg := hostplg.TestPlugin(t, conn, "test", "test")
unimplementedPlugin := hostplg.TestPlugin(t, conn, "unimplemented", "unimplemented")
// gotPluginAttrs tracks which attributes a plugin has received through a closure and can be compared in the
// test against the expected values sent to the plugin.
var gotPluginAttrs *structpb.Struct
plgm := map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &TestPluginServer{
OnCreateCatalogFn: func(_ context.Context, req *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) {
gotPluginAttrs = req.GetCatalog().GetAttributes()
return &plgpb.OnCreateCatalogResponse{Persisted: &plgpb.HostCatalogPersisted{Data: req.GetCatalog().GetSecrets()}}, nil
},
},
unimplementedPlugin.GetPublicId(): &plgpb.UnimplementedHostPluginServiceServer{},
}
tests := []struct {
name string
in *HostCatalog
opts []Option
want *HostCatalog
wantSecret []byte
wantSecret *structpb.Struct
wantIsErr errors.Code
}{
{
@@ -47,7 +66,7 @@ func TestRepository_CreateCatalog(t *testing.T) {
in: &HostCatalog{
HostCatalog: &store.HostCatalog{
PluginId: plg.GetPublicId(),
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
wantIsErr: errors.InvalidParameter,
@@ -57,7 +76,7 @@ func TestRepository_CreateCatalog(t *testing.T) {
in: &HostCatalog{
HostCatalog: &store.HostCatalog{
ScopeId: prj.GetPublicId(),
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
wantIsErr: errors.InvalidParameter,
@@ -78,14 +97,31 @@ func TestRepository_CreateCatalog(t *testing.T) {
HostCatalog: &store.HostCatalog{
ScopeId: prj.GetPublicId(),
PluginId: plg.GetPublicId(),
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
want: &HostCatalog{
HostCatalog: &store.HostCatalog{
ScopeId: prj.GetPublicId(),
PluginId: plg.GetPublicId(),
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
},
{
name: "valid-unimplemented-plugin",
in: &HostCatalog{
HostCatalog: &store.HostCatalog{
ScopeId: prj.GetPublicId(),
PluginId: unimplementedPlugin.GetPublicId(),
Attributes: []byte{},
},
},
want: &HostCatalog{
HostCatalog: &store.HostCatalog{
ScopeId: prj.GetPublicId(),
PluginId: unimplementedPlugin.GetPublicId(),
Attributes: []byte{},
},
},
},
@@ -95,7 +131,7 @@ func TestRepository_CreateCatalog(t *testing.T) {
HostCatalog: &store.HostCatalog{
ScopeId: prj.GetPublicId(),
PluginId: "unknown_plugin",
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
wantIsErr: errors.InvalidParameter,
@@ -107,7 +143,7 @@ func TestRepository_CreateCatalog(t *testing.T) {
Name: "test-name-repo",
ScopeId: prj.GetPublicId(),
PluginId: plg.GetPublicId(),
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
want: &HostCatalog{
@@ -115,7 +151,7 @@ func TestRepository_CreateCatalog(t *testing.T) {
Name: "test-name-repo",
ScopeId: prj.GetPublicId(),
PluginId: plg.GetPublicId(),
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
},
@@ -126,7 +162,7 @@ func TestRepository_CreateCatalog(t *testing.T) {
Description: "test-description-repo",
ScopeId: prj.GetPublicId(),
PluginId: plg.GetPublicId(),
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
want: &HostCatalog{
@@ -134,7 +170,36 @@ func TestRepository_CreateCatalog(t *testing.T) {
Description: "test-description-repo",
ScopeId: prj.GetPublicId(),
PluginId: plg.GetPublicId(),
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
},
{
name: "valid-with-attributes",
in: &HostCatalog{
HostCatalog: &store.HostCatalog{
ScopeId: prj.GetPublicId(),
PluginId: plg.GetPublicId(),
Attributes: func() []byte {
st, err := structpb.NewStruct(map[string]interface{}{"k1": "foo"})
require.NoError(t, err)
b, err := proto.Marshal(st)
require.NoError(t, err)
return b
}(),
},
},
want: &HostCatalog{
HostCatalog: &store.HostCatalog{
ScopeId: prj.GetPublicId(),
PluginId: plg.GetPublicId(),
Attributes: func() []byte {
st, err := structpb.NewStruct(map[string]interface{}{"k1": "foo"})
require.NoError(t, err)
b, err := proto.Marshal(st)
require.NoError(t, err)
return b
}(),
},
},
},
@@ -145,23 +210,35 @@ func TestRepository_CreateCatalog(t *testing.T) {
Description: "test-description-repo",
ScopeId: prj.GetPublicId(),
PluginId: plg.GetPublicId(),
Attributes: []byte("{}"),
},
secrets: map[string]interface{}{
"k1": "v1",
"k2": 2,
"k3": nil,
Attributes: []byte{},
},
secrets: func() *structpb.Struct {
st, err := structpb.NewStruct(map[string]interface{}{
"k1": "v1",
"k2": 2,
"k3": nil,
})
require.NoError(t, err)
return st
}(),
},
want: &HostCatalog{
HostCatalog: &store.HostCatalog{
Description: "test-description-repo",
ScopeId: prj.GetPublicId(),
PluginId: plg.GetPublicId(),
Attributes: []byte("{}"),
Attributes: []byte{},
},
},
wantSecret: []byte(`{"k1":"v1","k2":2,"k3":null}`),
wantSecret: func() *structpb.Struct {
st, err := structpb.NewStruct(map[string]interface{}{
"k1": "v1",
"k2": 2,
"k3": nil,
})
require.NoError(t, err)
return st
}(),
},
}
@@ -170,7 +247,7 @@ func TestRepository_CreateCatalog(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
assert := assert.New(t)
kmsCache := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(rw, rw, kmsCache)
repo, err := NewRepository(rw, rw, kmsCache, plgm)
assert.NoError(err)
assert.NotNil(repo)
got, err := repo.CreateCatalog(ctx, tt.in, tt.opts...)
@@ -188,6 +265,12 @@ func TestRepository_CreateCatalog(t *testing.T) {
assert.Equal(tt.want.Description, got.Description)
assert.Equal(got.CreateTime, got.UpdateTime)
// wantedPluginAttributes := &structpb.Struct{}
// require.NoError(t, proto.Unmarshal(tt.want.GetAttributes(), wantedPluginAttributes))
gotB, err := proto.Marshal(gotPluginAttrs)
require.NoError(t, err)
assert.Equal(tt.want.GetAttributes(), gotB)
assert.NoError(db.TestVerifyOplog(t, rw, got.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second)))
cSecret := allocHostCatalogSecret()
@@ -205,14 +288,17 @@ func TestRepository_CreateCatalog(t *testing.T) {
dbWrapper, err := kmsCache.GetWrapper(ctx, got.GetScopeId(), kms.KeyPurposeDatabase)
require.NoError(t, err)
require.NoError(t, cSecret.decrypt(ctx, dbWrapper))
assert.Equal(string(tt.wantSecret), string(cSecret.Secret))
st := &structpb.Struct{}
require.NoError(t, proto.Unmarshal(cSecret.Secret, st))
assert.Empty(cmp.Diff(tt.wantSecret, st, protocmp.Transform()))
})
}
t.Run("invalid-duplicate-names", func(t *testing.T) {
assert := assert.New(t)
kms := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(rw, rw, kms)
repo, err := NewRepository(rw, rw, kms, plgm)
assert.NoError(err)
assert.NotNil(repo)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
@@ -221,7 +307,7 @@ func TestRepository_CreateCatalog(t *testing.T) {
ScopeId: prj.GetPublicId(),
Name: "test-name-repo",
PluginId: plg.GetPublicId(),
Attributes: []byte("{}"),
Attributes: []byte{},
},
}
@@ -242,7 +328,7 @@ func TestRepository_CreateCatalog(t *testing.T) {
t.Run("valid-duplicate-names-diff-scopes", func(t *testing.T) {
assert := assert.New(t)
kms := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(rw, rw, kms)
repo, err := NewRepository(rw, rw, kms, plgm)
assert.NoError(err)
assert.NotNil(repo)
org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
@@ -250,7 +336,7 @@ func TestRepository_CreateCatalog(t *testing.T) {
HostCatalog: &store.HostCatalog{
Name: "test-name-repo",
PluginId: plg.GetPublicId(),
Attributes: []byte("{}"),
Attributes: []byte{},
},
}
in2 := in.clone()
@@ -292,6 +378,9 @@ func TestRepository_LookupCatalog(t *testing.T) {
wrapper := db.TestWrapper(t)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
plg := hostplg.TestPlugin(t, conn, "test", "test")
plgm := map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &TestPluginServer{},
}
cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
badId, err := newHostCatalogId(ctx, plg.GetIdPrefix())
assert.NoError(t, err)
@@ -326,7 +415,7 @@ func TestRepository_LookupCatalog(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
assert := assert.New(t)
kms := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(rw, rw, kms)
repo, err := NewRepository(rw, rw, kms, plgm)
assert.NoError(err)
assert.NotNil(repo)
@@ -354,10 +443,13 @@ func TestRepository_ListCatalogs_Multiple_Scopes(t *testing.T) {
wrapper := db.TestWrapper(t)
rw := db.New(conn)
kms := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(rw, rw, kms)
plg := hostplg.TestPlugin(t, conn, "test", "test")
plgm := map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &TestPluginServer{},
}
repo, err := NewRepository(rw, rw, kms, plgm)
assert.NoError(t, err)
assert.NotNil(t, repo)
plg := hostplg.TestPlugin(t, conn, "test", "test")
const numPerScope = 10
var projs []string
+50 -6
View File
@@ -14,6 +14,12 @@ import (
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
hostplg "github.com/hashicorp/boundary/internal/plugin/host"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
)
// CreateSet inserts s into the repository and returns a new HostSet
@@ -45,14 +51,15 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet,
}
s = s.clone()
c := allocHostCatalog()
c.PublicId = s.GetCatalogId()
if err := r.reader.LookupByPublicId(ctx, c); err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get host catalog"))
c, err := r.getCatalog(ctx, s.CatalogId)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("looking up catalog"))
}
per, err := r.getPersistedDataForCatalog(ctx, c)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("looking up persisted data"))
}
// TODO: Contain the following plugin logic inside a plugin manager as well as
// calling the plugin which we have looked up.
plg := hostplg.NewPlugin("", "")
plg.PublicId = c.GetPluginId()
if err := r.reader.LookupByPublicId(ctx, plg); err != nil {
@@ -65,6 +72,24 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet,
}
s.PublicId = id
plgClient, ok := r.plugins[plg.GetPublicId()]
if !ok || plgClient == nil {
return nil, errors.New(ctx, errors.Internal, op, fmt.Sprintf("plugin with plugin name %q not available", plg.GetPluginName()))
}
plgHc, err := toPluginCatalog(ctx, c)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
plgHs, err := toPluginSet(ctx, s)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if _, err := plgClient.OnCreateSet(ctx, &plgpb.OnCreateSetRequest{Catalog: plgHc, Set: plgHs, Persisted: per}); err != nil {
if status.Code(err) != codes.Unimplemented {
return nil, errors.Wrap(ctx, err, op)
}
}
oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
@@ -294,3 +319,22 @@ type hostSetAgg struct {
// TableName returns the table name for gorm
func (agg *hostSetAgg) TableName() string { return "host_plugin_host_set_with_value_obj" }
// toPluginSet returns a host set in the format expected by the host plugin system.
func toPluginSet(ctx context.Context, in *HostSet) (*pb.HostSet, error) {
const op = "plugin.toPluginCatalog"
if in == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil storage plugin")
}
hs := &pb.HostSet{
Id: in.GetPublicId(),
}
if in.GetAttributes() != nil {
attrs := &structpb.Struct{}
if err := proto.Unmarshal(in.GetAttributes(), attrs); err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to marshal attributes"))
}
hs.Attributes = attrs
}
return hs, nil
}
@@ -16,9 +16,12 @@ import (
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
hostplg "github.com/hashicorp/boundary/internal/plugin/host"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
"google.golang.org/protobuf/types/known/structpb"
)
func TestRepository_CreateSet(t *testing.T) {
@@ -29,8 +32,20 @@ func TestRepository_CreateSet(t *testing.T) {
iamRepo := iam.TestRepo(t, conn, wrapper)
_, prj := iam.TestScopes(t, iamRepo)
plg := hostplg.TestPlugin(t, conn, "create", "create")
unimplementedPlugin := hostplg.TestPlugin(t, conn, "unimplemented", "unimplemented")
var pluginReceivedAttrs *structpb.Struct
plgm := map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &TestPluginServer{OnCreateSetFn: func(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) {
pluginReceivedAttrs = req.GetSet().GetAttributes()
return &plgpb.OnCreateSetResponse{}, nil
}},
unimplementedPlugin.GetPublicId(): &plgpb.UnimplementedHostPluginServiceServer{},
}
catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
attrs := []byte("{}")
unimplementedPluginCatalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
attrs := []byte{}
tests := []struct {
name string
@@ -126,6 +141,23 @@ func TestRepository_CreateSet(t *testing.T) {
},
},
},
{
name: "valid-unimplemented-plugin",
in: &HostSet{
HostSet: &store.HostSet{
CatalogId: unimplementedPluginCatalog.PublicId,
Name: "valid-unimplemented-plugin",
Attributes: attrs,
},
},
want: &HostSet{
HostSet: &store.HostSet{
CatalogId: unimplementedPluginCatalog.PublicId,
Name: "valid-unimplemented-plugin",
Attributes: attrs,
},
},
},
{
name: "valid-with-description",
in: &HostSet{
@@ -143,13 +175,38 @@ func TestRepository_CreateSet(t *testing.T) {
},
},
},
{
name: "valid-with-custom-attributes",
in: &HostSet{
HostSet: &store.HostSet{
CatalogId: catalog.PublicId,
Description: ("test-description-repo"),
Attributes: func() []byte {
b, err := proto.Marshal(&structpb.Struct{Fields: map[string]*structpb.Value{"k1": structpb.NewStringValue("foo")}})
require.NoError(t, err)
return b
}(),
},
},
want: &HostSet{
HostSet: &store.HostSet{
CatalogId: catalog.PublicId,
Description: ("test-description-repo"),
Attributes: func() []byte {
b, err := proto.Marshal(&structpb.Struct{Fields: map[string]*structpb.Value{"k1": structpb.NewStringValue("foo")}})
require.NoError(t, err)
return b
}(),
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
repo, err := NewRepository(rw, rw, kms)
repo, err := NewRepository(rw, rw, kms, plgm)
require.NoError(err)
require.NotNil(repo)
got, err := repo.CreateSet(context.Background(), prj.GetPublicId(), tt.in, tt.opts...)
@@ -166,13 +223,17 @@ func TestRepository_CreateSet(t *testing.T) {
assert.Equal(tt.want.Name, got.GetName())
assert.Equal(tt.want.Description, got.GetDescription())
assert.Equal(got.GetCreateTime(), got.GetUpdateTime())
wantedPluginAttributes := &structpb.Struct{}
require.NoError(proto.Unmarshal(tt.want.Attributes, wantedPluginAttributes))
assert.Empty(cmp.Diff(wantedPluginAttributes, pluginReceivedAttrs, protocmp.Transform()))
assert.NoError(db.TestVerifyOplog(t, rw, got.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second)))
})
}
t.Run("invalid-duplicate-names", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
repo, err := NewRepository(rw, rw, kms)
repo, err := NewRepository(rw, rw, kms, plgm)
require.NoError(err)
require.NotNil(repo)
@@ -183,7 +244,7 @@ func TestRepository_CreateSet(t *testing.T) {
HostSet: &store.HostSet{
CatalogId: catalog.PublicId,
Name: "test-name-repo",
Attributes: []byte("{}"),
Attributes: []byte{},
},
}
@@ -203,7 +264,7 @@ func TestRepository_CreateSet(t *testing.T) {
t.Run("valid-duplicate-names-diff-catalogs", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
repo, err := NewRepository(rw, rw, kms)
repo, err := NewRepository(rw, rw, kms, plgm)
require.NoError(err)
require.NotNil(repo)
@@ -214,7 +275,7 @@ func TestRepository_CreateSet(t *testing.T) {
in := &HostSet{
HostSet: &store.HostSet{
Name: "test-name-repo",
Attributes: []byte("{}"),
Attributes: []byte{},
},
}
in2 := in.clone()
@@ -250,9 +311,14 @@ func TestRepository_LookupSet(t *testing.T) {
iamRepo := iam.TestRepo(t, conn, wrapper)
_, prj := iam.TestScopes(t, iamRepo)
plg := hostplg.TestPlugin(t, conn, "lookup", "lookup")
plgm := map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &TestPluginServer{},
}
catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
hostSet := TestSet(t, conn, kms, catalog)
hostSet := TestSet(t, conn, kms, catalog, map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &TestPluginServer{},
})
hostSetId, err := newHostSetId(ctx, plg.GetIdPrefix())
require.NoError(t, err)
@@ -281,7 +347,7 @@ func TestRepository_LookupSet(t *testing.T) {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
repo, err := NewRepository(rw, rw, kms)
repo, err := NewRepository(rw, rw, kms, plgm)
assert.NoError(err)
require.NotNil(repo)
got, err := repo.LookupSet(ctx, tt.in)
@@ -307,13 +373,16 @@ func TestRepository_ListSets(t *testing.T) {
_, prj := iam.TestScopes(t, iamRepo)
plg := hostplg.TestPlugin(t, conn, "list", "list")
plgm := map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &TestPluginServer{},
}
catalogA := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
catalogB := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
hostSets := []*HostSet{
TestSet(t, conn, kms, catalogA),
TestSet(t, conn, kms, catalogA),
TestSet(t, conn, kms, catalogA),
TestSet(t, conn, kms, catalogA, plgm),
TestSet(t, conn, kms, catalogA, plgm),
TestSet(t, conn, kms, catalogA, plgm),
}
tests := []struct {
@@ -343,7 +412,7 @@ func TestRepository_ListSets(t *testing.T) {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
repo, err := NewRepository(rw, rw, kms)
repo, err := NewRepository(rw, rw, kms, plgm)
assert.NoError(err)
require.NotNil(repo)
got, err := repo.ListSets(context.Background(), tt.in, tt.opts...)
@@ -371,11 +440,14 @@ func TestRepository_ListSets_Limits(t *testing.T) {
_, prj := iam.TestScopes(t, iamRepo)
plg := hostplg.TestPlugin(t, conn, "listlimit", "listlimit")
plgm := map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &TestPluginServer{},
}
catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
count := 10
var hostSets []*HostSet
for i := 0; i < count; i++ {
hostSets = append(hostSets, TestSet(t, conn, kms, catalog))
hostSets = append(hostSets, TestSet(t, conn, kms, catalog, plgm))
}
tests := []struct {
@@ -426,7 +498,7 @@ func TestRepository_ListSets_Limits(t *testing.T) {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
repo, err := NewRepository(rw, rw, kms, tt.repoOpts...)
repo, err := NewRepository(rw, rw, kms, plgm, tt.repoOpts...)
assert.NoError(err)
require.NotNil(repo)
got, err := repo.ListSets(context.Background(), hostSets[0].CatalogId, tt.listOpts...)
+47 -24
View File
@@ -3,6 +3,7 @@ package plugin
import (
"testing"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -18,11 +19,14 @@ func TestRepository_New(t *testing.T) {
wrapper := db.TestWrapper(t)
kmsCache := kms.TestKms(t, conn, wrapper)
plgs := map[string]plgpb.HostPluginServiceServer{}
type args struct {
r db.Reader
w db.Writer
kms *kms.Kms
opts []host.Option
r db.Reader
w db.Writer
kms *kms.Kms
plugins map[string]plgpb.HostPluginServiceServer
opts []host.Option
}
tests := []struct {
@@ -34,38 +38,43 @@ func TestRepository_New(t *testing.T) {
{
name: "valid",
args: args{
r: rw,
w: rw,
kms: kmsCache,
r: rw,
w: rw,
kms: kmsCache,
plugins: plgs,
},
want: &Repository{
reader: rw,
writer: rw,
kms: kmsCache,
plugins: plgs,
defaultLimit: db.DefaultLimit,
},
},
{
name: "valid-with-limit",
args: args{
r: rw,
w: rw,
kms: kmsCache,
opts: []host.Option{host.WithLimit(5)},
r: rw,
w: rw,
kms: kmsCache,
plugins: plgs,
opts: []host.Option{host.WithLimit(5)},
},
want: &Repository{
reader: rw,
writer: rw,
kms: kmsCache,
plugins: plgs,
defaultLimit: 5,
},
},
{
name: "nil-reader",
args: args{
r: nil,
w: rw,
kms: kmsCache,
r: nil,
w: rw,
kms: kmsCache,
plugins: plgs,
},
want: nil,
wantIsErr: errors.InvalidParameter,
@@ -73,9 +82,10 @@ func TestRepository_New(t *testing.T) {
{
name: "nil-writer",
args: args{
r: rw,
w: nil,
kms: kmsCache,
r: rw,
w: nil,
kms: kmsCache,
plugins: plgs,
},
want: nil,
wantIsErr: errors.InvalidParameter,
@@ -83,9 +93,21 @@ func TestRepository_New(t *testing.T) {
{
name: "nil-kms",
args: args{
r: rw,
w: rw,
kms: nil,
r: rw,
w: rw,
kms: nil,
plugins: plgs,
},
want: nil,
wantIsErr: errors.InvalidParameter,
},
{
name: "nil-plugins",
args: args{
r: rw,
w: rw,
kms: kmsCache,
plugins: nil,
},
want: nil,
wantIsErr: errors.InvalidParameter,
@@ -93,9 +115,10 @@ func TestRepository_New(t *testing.T) {
{
name: "all-nils",
args: args{
r: nil,
w: nil,
kms: nil,
r: nil,
w: nil,
kms: nil,
plugins: nil,
},
want: nil,
wantIsErr: errors.InvalidParameter,
@@ -105,7 +128,7 @@ func TestRepository_New(t *testing.T) {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
got, err := NewRepository(tt.args.r, tt.args.w, tt.args.kms, tt.args.opts...)
got, err := NewRepository(tt.args.r, tt.args.w, tt.args.kms, tt.args.plugins, tt.args.opts...)
if tt.wantIsErr != 0 {
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err)
assert.Nil(got)
+5 -5
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.15.8
// protoc v3.17.3
// source: controller/storage/host/plugin/store/v1/host.proto
// Package store provides protobufs for storing types in the plugin host
@@ -56,8 +56,8 @@ type HostCatalog struct {
// @inject_tag: `gorm:"default:null"`
Version uint32 `protobuf:"varint,8,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"`
// attributes is a jsonb formatted field.
// @inject_tag: `gorm:"default:null"`
Attributes []byte `protobuf:"bytes,9,opt,name=attributes,proto3" json:"attributes,omitempty" gorm:"default:null"`
// @inject_tag: `gorm:"not_null"`
Attributes []byte `protobuf:"bytes,9,opt,name=attributes,proto3" json:"attributes,omitempty" gorm:"not_null"`
}
func (x *HostCatalog) Reset() {
@@ -184,8 +184,8 @@ type HostSet struct {
// @inject_tag: `gorm:"default:null"`
Version uint32 `protobuf:"varint,7,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"`
// attributes is a byte field containing marshaled JSON data
// @inject_tag: `gorm:"default:null"`
Attributes []byte `protobuf:"bytes,8,opt,name=attributes,proto3" json:"attributes,omitempty" gorm:"default:null"`
// @inject_tag: `gorm:"not_null"`
Attributes []byte `protobuf:"bytes,8,opt,name=attributes,proto3" json:"attributes,omitempty" gorm:"not_null"`
// preferred_endpoints stores string preference values
// @inject_tag: `gorm:"-"`
PreferredEndpoints []string `protobuf:"bytes,9,rep,name=preferred_endpoints,json=preferredEndpoints,proto3" json:"preferred_endpoints,omitempty" gorm:"-"`
+64 -2
View File
@@ -7,6 +7,7 @@ import (
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/plugin/host"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/jinzhu/gorm"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -40,13 +41,13 @@ func TestCatalog(t *testing.T, conn *gorm.DB, scopeId, pluginId string, opt ...O
// TestSet creates a plugin host sets in the provided DB
// with the provided catalog id. The catalog must have been created
// previously. The test will fail if any errors are encountered.
func TestSet(t *testing.T, conn *gorm.DB, kmsCache *kms.Kms, hc *HostCatalog, opt ...Option) *HostSet {
func TestSet(t *testing.T, conn *gorm.DB, kmsCache *kms.Kms, hc *HostCatalog, plgm map[string]plgpb.HostPluginServiceServer, opt ...Option) *HostSet {
t.Helper()
require := require.New(t)
ctx := context.Background()
rw := db.New(conn)
repo, err := NewRepository(rw, rw, kmsCache)
repo, err := NewRepository(rw, rw, kmsCache, plgm)
require.NoError(err)
set, err := NewHostSet(ctx, hc.PublicId, opt...)
@@ -66,3 +67,64 @@ func TestSet(t *testing.T, conn *gorm.DB, kmsCache *kms.Kms, hc *HostCatalog, op
return set
}
// TestPluginServer provides a host plugin service server where each method can be overwritten for tests.
type TestPluginServer struct {
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)
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) 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)
}
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) 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)
}
+2 -1
View File
@@ -9,6 +9,7 @@ import (
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/plugin/host"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -46,7 +47,7 @@ func Test_TestSet(t *testing.T) {
assert.NotEmpty(plg.GetPublicId())
c := TestCatalog(t, conn, prj.GetPublicId(), plg.GetPublicId())
set := TestSet(t, conn, kmsCache, c, WithName("foo"), WithDescription("bar"))
set := TestSet(t, conn, kmsCache, c, map[string]plgpb.HostPluginServiceServer{plg.GetPublicId(): &TestPluginServer{}}, WithName("foo"), WithDescription("bar"))
assert.NotEmpty(set.GetPublicId())
assert.True(strings.HasPrefix(set.GetPublicId(), fmt.Sprintf("%s_%s", HostSetPrefix, "prefix")))
assert.Equal("foo", set.GetName())
+7 -2
View File
@@ -11,6 +11,7 @@ import (
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
hostplg "github.com/hashicorp/boundary/internal/plugin/host"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -26,7 +27,9 @@ func TestPreferredEndpoint_Create(t *testing.T) {
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
plg := hostplg.TestPlugin(t, conn, "create", "create")
catalog := plugin.TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
set := plugin.TestSet(t, conn, kmsCache, catalog)
set := plugin.TestSet(t, conn, kmsCache, catalog, map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &plugin.TestPluginServer{},
})
type args struct {
hostSetId string
@@ -154,7 +157,9 @@ func TestPreferredEndpoint_Delete(t *testing.T) {
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
plg := hostplg.TestPlugin(t, conn, "create", "create")
catalog := plugin.TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
set := plugin.TestSet(t, conn, kmsCache, catalog)
set := plugin.TestSet(t, conn, kmsCache, catalog, map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &plgpb.UnimplementedHostPluginServiceServer{},
})
peFunc := func(priority uint32, condition string) *host.PreferredEndpoint {
ep, err := host.NewPreferredEndpoint(ctx, set.PublicId, priority, condition)
+1 -1
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.15.8
// protoc v3.17.3
// source: controller/storage/plugin/host/store/v1/plugin.proto
// Package store provides protobufs for storing types in the host package.
+1 -1
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.15.8
// protoc v3.17.3
// source: controller/storage/plugin/store/v1/plugin.proto
// Package store provides protobufs for storing types in the host package.
@@ -43,7 +43,7 @@ message HostCatalog {
uint32 version = 8;
// attributes is a jsonb formatted field.
// @inject_tag: `gorm:"default:null"`
// @inject_tag: `gorm:"not_null"`
bytes attributes = 9;
}
@@ -79,7 +79,7 @@ message HostSet {
uint32 version = 7;
// attributes is a byte field containing marshaled JSON data
// @inject_tag: `gorm:"default:null"`
// @inject_tag: `gorm:"not_null"`
bytes attributes = 8;
// preferred_endpoints stores string preference values
+2 -1
View File
@@ -24,6 +24,7 @@ import (
"github.com/hashicorp/boundary/internal/servers/controller/common"
"github.com/hashicorp/boundary/internal/session"
"github.com/hashicorp/boundary/internal/target"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-secure-stdlib/mlock"
"github.com/patrickmn/go-cache"
@@ -136,7 +137,7 @@ func New(ctx context.Context, conf *Config) (*Controller, error) {
return static.NewRepository(dbase, dbase, c.kms)
}
c.PluginHostRepoFn = func() (*hostplugin.Repository, error) {
return hostplugin.NewRepository(dbase, dbase, c.kms)
return hostplugin.NewRepository(dbase, dbase, c.kms, map[string]plgpb.HostPluginServiceServer{})
}
c.HostPluginRepoFn = func() (*host.Repository, error) {
return host.NewRepository(dbase, dbase, c.kms)
@@ -2,7 +2,6 @@ package host_catalogs
import (
"context"
"encoding/json"
"fmt"
"strings"
@@ -26,6 +25,7 @@ import (
"github.com/hashicorp/boundary/internal/types/scope"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/wrapperspb"
)
@@ -603,16 +603,13 @@ func toProto(ctx context.Context, in host.Catalog, opt ...handlers.Option) (*pb.
if outputFields.Has(globals.AttributesField) {
switch h := in.(type) {
case *plugin.HostCatalog:
attrs := map[string]interface{}{}
err := json.Unmarshal(h.Attributes, &attrs)
attrs := &structpb.Struct{}
err := proto.Unmarshal(h.Attributes, attrs)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if len(attrs) > 0 {
out.Attributes, err = structpb.NewStruct(attrs)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if len(attrs.GetFields()) > 0 {
out.Attributes = attrs
}
}
}
@@ -645,10 +642,10 @@ func toStoragePluginCatalog(ctx context.Context, scopeId, plgId string, item *pb
opts = append(opts, plugin.WithDescription(desc.GetValue()))
}
if attrs := item.GetAttributes(); attrs != nil {
opts = append(opts, plugin.WithAttributes(attrs.AsMap()))
opts = append(opts, plugin.WithAttributes(attrs))
}
if secrets := item.GetSecrets(); secrets != nil {
opts = append(opts, plugin.WithSecrets(secrets.AsMap()))
opts = append(opts, plugin.WithSecrets(secrets))
}
hc, err := plugin.NewHostCatalog(ctx, scopeId, plgId, opts...)
if err != nil {
@@ -23,6 +23,7 @@ import (
"github.com/hashicorp/boundary/internal/types/scope"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs"
scopepb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"google.golang.org/genproto/protobuf/field_mask"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/proto"
@@ -62,7 +63,7 @@ func TestGet_Static(t *testing.T) {
return static.NewRepository(rw, rw, kms)
}
pluginHostRepo := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
}
pluginRepo := func() (*host.Repository, error) {
return host.NewRepository(rw, rw, kms)
@@ -151,7 +152,7 @@ func TestGet_Plugin(t *testing.T) {
return static.NewRepository(rw, rw, kms)
}
pluginHostRepo := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
}
pluginRepo := func() (*host.Repository, error) {
return host.NewRepository(rw, rw, kms)
@@ -240,7 +241,7 @@ func TestList(t *testing.T) {
return iam.TestRepo(t, conn, wrapper), nil
}
pluginHostRepo := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
}
pluginRepo := func() (*host.Repository, error) {
return host.NewRepository(rw, rw, kms)
@@ -433,7 +434,7 @@ func TestDelete(t *testing.T) {
return static.NewRepository(rw, rw, kms)
}
pluginHostRepo := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
}
pluginRepo := func() (*host.Repository, error) {
return host.NewRepository(rw, rw, kms)
@@ -503,7 +504,7 @@ func TestDelete_twice(t *testing.T) {
return static.NewRepository(rw, rw, kms)
}
pluginHostRepo := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
}
pluginRepo := func() (*host.Repository, error) {
return host.NewRepository(rw, rw, kms)
@@ -538,7 +539,7 @@ func TestCreate_Static(t *testing.T) {
return static.NewRepository(rw, rw, kms)
}
pluginHostRepo := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
}
pluginRepo := func() (*host.Repository, error) {
return host.NewRepository(rw, rw, kms)
@@ -688,9 +689,6 @@ func TestCreate_Plugin(t *testing.T) {
repo := func() (*static.Repository, error) {
return static.NewRepository(rw, rw, kms)
}
pluginHostRepo := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
}
pluginRepo := func() (*host.Repository, error) {
return host.NewRepository(rw, rw, kms)
}
@@ -700,6 +698,15 @@ func TestCreate_Plugin(t *testing.T) {
name := "test"
plg := host.TestPlugin(t, conn, name, name)
pluginHostRepo := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &plugin.TestPluginServer{
OnCreateCatalogFn: func(ctx context.Context, req *plgpb.OnCreateCatalogRequest) (*plgpb.OnCreateCatalogResponse, error) {
return nil, nil
},
},
})
}
defaultHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
defaultHcCreated := defaultHc.GetCreateTime().GetTimestamp().AsTime()
toMerge := &pbs.CreateHostCatalogRequest{}
@@ -845,7 +852,7 @@ func TestUpdate(t *testing.T) {
return static.NewRepository(rw, rw, kms)
}
pluginHostRepo := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
}
pluginRepo := func() (*host.Repository, error) {
return host.NewRepository(rw, rw, kms)
@@ -2,7 +2,6 @@ package host_sets
import (
"context"
"encoding/json"
"fmt"
"strings"
@@ -24,6 +23,7 @@ import (
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets"
"github.com/hashicorp/go-secure-stdlib/strutil"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/wrapperspb"
)
@@ -756,16 +756,13 @@ func toProto(ctx context.Context, in host.Set, hosts []host.Host, opt ...handler
out.PreferredEndpoints = h.GetPreferredEndpoints()
}
if outputFields.Has(globals.AttributesField) {
attrs := map[string]interface{}{}
err := json.Unmarshal(h.Attributes, &attrs)
attrs := &structpb.Struct{}
err := proto.Unmarshal(h.Attributes, attrs)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if len(attrs) > 0 {
out.Attributes, err = structpb.NewStruct(attrs)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if len(attrs.GetFields()) > 0 {
out.Attributes = attrs
}
}
}
@@ -799,7 +796,7 @@ func toStoragePluginSet(ctx context.Context, catalogId string, item *pb.HostSet)
opts = append(opts, plugin.WithDescription(item.GetDescription().GetValue()))
}
if item.GetAttributes() != nil {
opts = append(opts, plugin.WithAttributes(item.GetAttributes().AsMap()))
opts = append(opts, plugin.WithAttributes(item.GetAttributes()))
}
if item.GetPreferredEndpoints() != nil {
opts = append(opts, plugin.WithPreferredEndpoints(item.GetPreferredEndpoints()))
@@ -23,6 +23,7 @@ import (
"github.com/hashicorp/boundary/internal/types/scope"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/genproto/protobuf/field_mask"
@@ -54,7 +55,7 @@ func TestGet_Static(t *testing.T) {
return static.NewRepository(rw, rw, kms)
}
pluginRepoFn := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
}
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
@@ -147,14 +148,16 @@ func TestGet_Plugin(t *testing.T) {
return static.NewRepository(rw, rw, kms)
}
pluginRepoFn := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
}
name := "test"
prefEndpoints := []string{"cidr:1.2.3.4", "cidr:2.3.4.5/24"}
plg := hostplugin.TestPlugin(t, conn, name, name)
hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
hs := plugin.TestSet(t, conn, kms, hc, plugin.WithPreferredEndpoints(prefEndpoints))
hs := plugin.TestSet(t, conn, kms, hc, map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &plugin.TestPluginServer{},
}, plugin.WithPreferredEndpoints(prefEndpoints))
toMerge := &pbs.GetHostSetRequest{}
@@ -241,7 +244,7 @@ func TestList_Static(t *testing.T) {
return static.NewRepository(rw, rw, kms)
}
pluginRepoFn := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
}
hcs := static.TestCatalogs(t, conn, proj.GetPublicId(), 2)
hc, hcNoHosts := hcs[0], hcs[1]
@@ -338,18 +341,21 @@ func TestList_Plugin(t *testing.T) {
repoFn := func() (*static.Repository, error) {
return static.NewRepository(rw, rw, kms)
}
pluginRepoFn := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
}
name := "test"
plg := hostplugin.TestPlugin(t, conn, name, name)
plgm := map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &plugin.TestPluginServer{},
}
pluginRepoFn := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms, plgm)
}
hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
hcNoHosts := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
preferredEndpoints := []string{"cidr:1.2.3.4", "dns:*.foobar.com"}
var wantHs []*pb.HostSet
for i := 0; i < 10; i++ {
h := plugin.TestSet(t, conn, kms, hc, plugin.WithPreferredEndpoints(preferredEndpoints))
h := plugin.TestSet(t, conn, kms, hc, plgm, plugin.WithPreferredEndpoints(preferredEndpoints))
wantHs = append(wantHs, &pb.HostSet{
Id: h.GetPublicId(),
HostCatalogId: h.GetCatalogId(),
@@ -443,7 +449,7 @@ func TestDelete(t *testing.T) {
return static.NewRepository(rw, rw, kms)
}
pluginRepoFn := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
}
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
h := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
@@ -522,7 +528,7 @@ func TestDelete_twice(t *testing.T) {
return static.NewRepository(rw, rw, kms)
}
plgRepoFn := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
}
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
h := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
@@ -558,7 +564,7 @@ func TestCreate_Static(t *testing.T) {
return static.NewRepository(rw, rw, kms)
}
plgRepoFn := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
}
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
@@ -700,11 +706,17 @@ func TestCreate_Plugin(t *testing.T) {
repoFn := func() (*static.Repository, error) {
return static.NewRepository(rw, rw, kms)
}
plgRepoFn := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
}
name := "test"
plg := hostplugin.TestPlugin(t, conn, name, name)
plgRepoFn := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &plugin.TestPluginServer{
OnCreateSetFn: func(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) {
return nil, nil
},
},
})
}
hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
testAttrs, err := structpb.NewStruct(map[string]interface{}{
@@ -956,7 +968,7 @@ func TestUpdate(t *testing.T) {
}
plgRepoFn := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
}
tested, err := host_sets.NewService(repoFn, plgRepoFn)
require.NoError(t, err, "Failed to create a new host set service.")
@@ -1295,7 +1307,7 @@ func TestAddHostSetHosts(t *testing.T) {
return static.NewRepository(rw, rw, kms)
}
plgRepoFn := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
}
s, err := host_sets.NewService(repoFn, plgRepoFn)
require.NoError(t, err, "Error when getting new host set service.")
@@ -1415,7 +1427,7 @@ func TestSetHostSetHosts(t *testing.T) {
return static.NewRepository(rw, rw, kms)
}
plgRepoFn := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
}
s, err := host_sets.NewService(repoFn, plgRepoFn)
require.NoError(t, err, "Error when getting new host set service.")
@@ -1531,7 +1543,7 @@ func TestRemoveHostSetHosts(t *testing.T) {
return static.NewRepository(rw, rw, kms)
}
plgRepoFn := func() (*plugin.Repository, error) {
return plugin.NewRepository(rw, rw, kms)
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
}
s, err := host_sets.NewService(repoFn, plgRepoFn)
require.NoError(t, err, "Error when getting new host set service.")
@@ -38,6 +38,7 @@ import (
credpb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentiallibraries"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/jinzhu/gorm"
"github.com/stretchr/testify/assert"
@@ -983,7 +984,9 @@ func TestAddTargetHostSets(t *testing.T) {
plg := host.TestPlugin(t, conn, "test", "test")
pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
pluginHs := plugin.TestSet(t, conn, kms, pluginHc)
pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &plugin.TestPluginServer{},
})
addCases := []struct {
name string
@@ -1115,7 +1118,9 @@ func TestSetTargetHostSets(t *testing.T) {
plg := host.TestPlugin(t, conn, "test", "test")
pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
pluginHs := plugin.TestSet(t, conn, kms, pluginHc)
pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &plugin.TestPluginServer{},
})
setCases := []struct {
name string
@@ -1241,7 +1246,9 @@ func TestRemoveTargetHostSets(t *testing.T) {
plg := host.TestPlugin(t, conn, "test", "test")
pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
pluginHs := plugin.TestSet(t, conn, kms, pluginHc)
pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &plugin.TestPluginServer{},
})
removeCases := []struct {
name string
@@ -1379,7 +1386,9 @@ func TestAddTargetHostSources(t *testing.T) {
plg := host.TestPlugin(t, conn, "test", "test")
pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
pluginHs := plugin.TestSet(t, conn, kms, pluginHc)
pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &plugin.TestPluginServer{},
})
addCases := []struct {
name string
@@ -1511,7 +1520,9 @@ func TestSetTargetHostSources(t *testing.T) {
plg := host.TestPlugin(t, conn, "test", "test")
pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
pluginHs := plugin.TestSet(t, conn, kms, pluginHc)
pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &plugin.TestPluginServer{},
})
setCases := []struct {
name string
@@ -1631,7 +1642,9 @@ func TestRemoveTargetHostSources(t *testing.T) {
plg := host.TestPlugin(t, conn, "test", "test")
pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
pluginHs := plugin.TestSet(t, conn, kms, pluginHc)
pluginHs := plugin.TestSet(t, conn, kms, pluginHc, map[string]plgpb.HostPluginServiceServer{
plg.GetPublicId(): &plugin.TestPluginServer{},
})
removeCases := []struct {
name string
+1 -1
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.15.8
// protoc v3.17.3
// source: plugin/v1/host_plugin_service.proto
package plugin