Add Plugin Host Set Repo Create, List, Read Operations (#1497)
Add repo for plugin based host sets as well as tests.
This commit is contained in:
1 parent
7769452f3b
commit
1249f9668f
13 files changed
+819
-43
No files matched your search
@@ -1,7 +1,7 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/boundary"
|
||||
"github.com/hashicorp/boundary/internal/types/subtypes"
|
||||
)
|
||||
|
||||
@@ -28,32 +28,17 @@ func Register(subtype subtypes.Subtype, prefixes ...string) error {
|
||||
|
||||
// AuthMethod contains the common methods across all the different types of auth methods.
|
||||
type AuthMethod interface {
|
||||
GetPublicId() string
|
||||
GetCreateTime() *timestamp.Timestamp
|
||||
GetUpdateTime() *timestamp.Timestamp
|
||||
GetName() string
|
||||
GetDescription() string
|
||||
boundary.Resource
|
||||
GetScopeId() string
|
||||
GetVersion() uint32
|
||||
GetIsPrimaryAuthMethod() bool
|
||||
}
|
||||
|
||||
type Account interface {
|
||||
GetPublicId() string
|
||||
GetCreateTime() *timestamp.Timestamp
|
||||
GetUpdateTime() *timestamp.Timestamp
|
||||
GetName() string
|
||||
GetDescription() string
|
||||
boundary.Resource
|
||||
GetAuthMethodId() string
|
||||
GetVersion() uint32
|
||||
}
|
||||
|
||||
type ManagedGroup interface {
|
||||
GetPublicId() string
|
||||
GetCreateTime() *timestamp.Timestamp
|
||||
GetUpdateTime() *timestamp.Timestamp
|
||||
GetName() string
|
||||
GetDescription() string
|
||||
boundary.Resource
|
||||
GetAuthMethodId() string
|
||||
GetVersion() uint32
|
||||
}
|
||||
@@ -44,6 +44,12 @@ func NewHostCatalog(ctx context.Context, pluginId, scopeId string, opt ...Option
|
||||
return hc, nil
|
||||
}
|
||||
|
||||
func allocHostCatalog() *HostCatalog {
|
||||
return &HostCatalog{
|
||||
HostCatalog: &store.HostCatalog{},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *HostCatalog) clone() *HostCatalog {
|
||||
cp := proto.Clone(c.HostCatalog)
|
||||
return &HostCatalog{
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
)
|
||||
|
||||
func TestHostCatalog_Create(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
@@ -140,21 +141,21 @@ func TestHostCatalog_Create(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := NewHostCatalog(context.Background(), tt.args.pluginId, tt.args.scopeId, tt.args.opts...)
|
||||
got, err := NewHostCatalog(ctx, tt.args.pluginId, tt.args.scopeId, tt.args.opts...)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
|
||||
assert.Emptyf(t, got.PublicId, "PublicId set")
|
||||
assert.Equal(t, tt.want, got)
|
||||
|
||||
id, err := newHostCatalogId()
|
||||
id, err := newHostCatalogId(plg.GetIdPrefix())
|
||||
assert.NoError(t, err)
|
||||
|
||||
tt.want.PublicId = id
|
||||
got.PublicId = id
|
||||
|
||||
w := db.New(conn)
|
||||
err = w.Create(context.Background(), got)
|
||||
err = w.Create(ctx, got)
|
||||
if tt.wantCreateErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
@@ -164,7 +165,7 @@ func TestHostCatalog_Create(t *testing.T) {
|
||||
PublicId: id,
|
||||
},
|
||||
}
|
||||
require.NoError(t, w.LookupById(context.Background(), found))
|
||||
require.NoError(t, w.LookupById(ctx, found))
|
||||
assert.Empty(t, cmp.Diff(got.HostCatalog, found.HostCatalog, protocmp.Transform()), "%q compared to %q", got.Attributes, found.Attributes)
|
||||
}
|
||||
})
|
||||
@@ -181,28 +182,28 @@ func TestHostCatalog_Create_DuplicateNames(t *testing.T) {
|
||||
plg := host.TestPlugin(t, conn, "test1", "prefix1")
|
||||
plg2 := host.TestPlugin(t, conn, "test2", "prefix2")
|
||||
|
||||
got, err := NewHostCatalog(context.Background(), plg.GetPublicId(), prj.GetPublicId(), WithName("duplicate"))
|
||||
got, err := NewHostCatalog(ctx, plg.GetPublicId(), prj.GetPublicId(), WithName("duplicate"))
|
||||
require.NoError(t, err)
|
||||
got.PublicId, err = newHostCatalogId()
|
||||
got.PublicId, err = newHostCatalogId(plg.GetIdPrefix())
|
||||
require.NoError(t, err)
|
||||
w.Create(ctx, got)
|
||||
|
||||
// Can't create another resource with the same pluginName in the same scope
|
||||
got.PublicId, err = newHostCatalogId()
|
||||
got.PublicId, err = newHostCatalogId(plg.GetIdPrefix())
|
||||
require.NoError(t, err)
|
||||
assert.Error(t, w.Create(ctx, got))
|
||||
|
||||
// Can't create another resource with same pluginName in same scope even for different plugin
|
||||
got, err = NewHostCatalog(context.Background(), plg2.GetPublicId(), prj.GetPublicId(), WithName("duplicate"))
|
||||
got, err = NewHostCatalog(ctx, plg2.GetPublicId(), prj.GetPublicId(), WithName("duplicate"))
|
||||
require.NoError(t, err)
|
||||
got.PublicId, err = newHostCatalogId()
|
||||
got.PublicId, err = newHostCatalogId(plg2.GetIdPrefix())
|
||||
require.NoError(t, err)
|
||||
assert.Error(t, w.Create(ctx, got))
|
||||
|
||||
// Can create another resource with same pluginName in different scope even for same plugin
|
||||
got, err = NewHostCatalog(context.Background(), plg.GetPublicId(), prj2.GetPublicId(), WithName("duplicate"))
|
||||
got, err = NewHostCatalog(ctx, plg.GetPublicId(), prj2.GetPublicId(), WithName("duplicate"))
|
||||
require.NoError(t, err)
|
||||
got.PublicId, err = newHostCatalogId()
|
||||
got.PublicId, err = newHostCatalogId(plg.GetIdPrefix())
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, w.Create(ctx, got))
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// A HostSet is a collection of hosts from the set's catalog.
|
||||
@@ -51,3 +53,28 @@ func (s *HostSet) TableName() string {
|
||||
func (s *HostSet) SetTableName(n string) {
|
||||
s.tableName = n
|
||||
}
|
||||
|
||||
func allocHostSet() *HostSet {
|
||||
return &HostSet{
|
||||
HostSet: &store.HostSet{},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HostSet) clone() *HostSet {
|
||||
cp := proto.Clone(s.HostSet)
|
||||
return &HostSet{
|
||||
HostSet: cp.(*store.HostSet),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HostSet) oplog(op oplog.OpType) oplog.Metadata {
|
||||
metadata := oplog.Metadata{
|
||||
"resource-public-id": []string{s.PublicId},
|
||||
"resource-type": []string{"plugin-host-set"},
|
||||
"op-type": []string{op.String()},
|
||||
}
|
||||
if s.CatalogId != "" {
|
||||
metadata["catalog-id"] = []string{s.CatalogId}
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
@@ -145,7 +145,7 @@ func TestHostSet_Create(t *testing.T) {
|
||||
assert.Emptyf(t, got.PublicId, "PublicId set")
|
||||
assert.Equal(t, tt.want, got)
|
||||
|
||||
id, err := newHostSetId()
|
||||
id, err := newHostSetId(plg.GetIdPrefix())
|
||||
assert.NoError(t, err)
|
||||
|
||||
tt.want.PublicId = id
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/host"
|
||||
@@ -22,18 +24,18 @@ const (
|
||||
Subtype = subtypes.Subtype("plugin")
|
||||
)
|
||||
|
||||
func newHostCatalogId() (string, error) {
|
||||
// TODO: Take in a prefix portion as an argument.
|
||||
id, err := db.NewPublicId(HostCatalogPrefix)
|
||||
func newHostCatalogId(upre string) (string, error) {
|
||||
prefix := fmt.Sprintf("%s_%s", HostCatalogPrefix, upre)
|
||||
id, err := db.NewPublicId(prefix)
|
||||
if err != nil {
|
||||
return "", errors.WrapDeprecated(err, "plugin.newHostCatalogId")
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func newHostSetId() (string, error) {
|
||||
// TODO: Take in a prefix portion as an argument.
|
||||
id, err := db.NewPublicId(HostSetPrefix)
|
||||
func newHostSetId(upre string) (string, error) {
|
||||
prefix := fmt.Sprintf("%s_%s", HostSetPrefix, upre)
|
||||
id, err := db.NewPublicId(prefix)
|
||||
if err != nil {
|
||||
return "", errors.WrapDeprecated(err, "plugin.newHostSetId")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
)
|
||||
|
||||
// A Repository stores and retrieves the persistent types in the plugin
|
||||
// package. It is not safe to use a repository concurrently.
|
||||
type Repository struct {
|
||||
reader db.Reader
|
||||
writer db.Writer
|
||||
kms *kms.Kms
|
||||
// defaultLimit provides a default for limiting the number of results
|
||||
// returned from the repo
|
||||
defaultLimit int
|
||||
}
|
||||
|
||||
// NewRepository creates a new Repository. The returned repository should
|
||||
// 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 ...Option) (*Repository, error) {
|
||||
const op = "plugin.NewRepository"
|
||||
switch {
|
||||
case r == nil:
|
||||
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "db.Reader")
|
||||
case w == nil:
|
||||
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "db.Writer")
|
||||
case kms == nil:
|
||||
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "kms")
|
||||
}
|
||||
|
||||
opts := getOpts(opt...)
|
||||
if opts.withLimit == 0 {
|
||||
// zero signals the boundary defaults should be used.
|
||||
opts.withLimit = db.DefaultLimit
|
||||
}
|
||||
|
||||
return &Repository{
|
||||
reader: r,
|
||||
writer: w,
|
||||
kms: kms,
|
||||
defaultLimit: opts.withLimit,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
hostplg "github.com/hashicorp/boundary/internal/plugin/host"
|
||||
)
|
||||
|
||||
// CreateSet inserts s into the repository and returns a new HostSet
|
||||
// containing the host set's PublicId. s is not changed. s must contain a
|
||||
// valid CatalogId. s must not contain a PublicId. The PublicId is
|
||||
// generated and assigned by this method. opt is ignored.
|
||||
//
|
||||
// Both s.Name and s.Description are optional. If s.Name is set, it must be
|
||||
// unique within s.CatalogId.
|
||||
func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet, _ ...Option) (*HostSet, error) {
|
||||
const op = "plugin.(Repository).CreateSet"
|
||||
if s == nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil HostSet")
|
||||
}
|
||||
if s.HostSet == nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil embedded HostSet")
|
||||
}
|
||||
if s.CatalogId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no catalog id")
|
||||
}
|
||||
if s.PublicId != "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty")
|
||||
}
|
||||
if scopeId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id")
|
||||
}
|
||||
if s.Attributes == nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil attributes")
|
||||
}
|
||||
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"))
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get host plugin"))
|
||||
}
|
||||
|
||||
id, err := newHostSetId(plg.GetIdPrefix())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
s.PublicId = id
|
||||
|
||||
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"))
|
||||
}
|
||||
|
||||
var newHostSet *HostSet
|
||||
_, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
|
||||
func(_ db.Reader, w db.Writer) error {
|
||||
newHostSet = s.clone()
|
||||
err := w.Create(ctx, newHostSet, db.WithOplog(oplogWrapper, s.oplog(oplog.OpType_OP_TYPE_CREATE)))
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if errors.IsUniqueError(err) {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in catalog: %s: name %s already exists", s.CatalogId, s.Name)))
|
||||
}
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in catalog: %s", s.CatalogId)))
|
||||
}
|
||||
return newHostSet, nil
|
||||
}
|
||||
|
||||
// LookupSet will look up a host set in the repository and return the host
|
||||
// set. If the host set is not found, it will return nil, nil.
|
||||
// All options are ignored.
|
||||
func (r *Repository) LookupSet(ctx context.Context, publicId string, _ ...Option) (*HostSet, error) {
|
||||
const op = "plugin.(Repository).LookupSet"
|
||||
if publicId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id")
|
||||
}
|
||||
|
||||
s := allocHostSet()
|
||||
s.PublicId = publicId
|
||||
|
||||
err := r.reader.LookupByPublicId(ctx, s)
|
||||
if errors.IsNotFoundError(err) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in %s", s.PublicId)))
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// ListSets returns a slice of HostSets for the catalogId. WithLimit is the
|
||||
// only option supported.
|
||||
func (r *Repository) ListSets(ctx context.Context, catalogId string, opt ...Option) ([]*HostSet, error) {
|
||||
const op = "plugin.(Repository).ListSets"
|
||||
if catalogId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no catalog id")
|
||||
}
|
||||
opts := getOpts(opt...)
|
||||
limit := r.defaultLimit
|
||||
if opts.withLimit != 0 {
|
||||
// non-zero signals an override of the default limit for the repo.
|
||||
limit = opts.withLimit
|
||||
}
|
||||
var sets []*HostSet
|
||||
err := r.reader.SearchWhere(ctx, &sets, "catalog_id = ?", []interface{}{catalogId}, db.WithLimit(limit))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
return sets, nil
|
||||
}
|
||||
@@ -0,0 +1,416 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/host/plugin/store"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
hostplg "github.com/hashicorp/boundary/internal/plugin/host"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
)
|
||||
|
||||
func TestRepository_CreateSet(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
plg := hostplg.TestPlugin(t, conn, "create", "create")
|
||||
catalog := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId)
|
||||
attrs := []byte("{}")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
in *HostSet
|
||||
opts []Option
|
||||
want *HostSet
|
||||
wantIsErr errors.Code
|
||||
}{
|
||||
{
|
||||
name: "nil-HostSet",
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "nil-embedded-HostSet",
|
||||
in: &HostSet{},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "invalid-no-catalog-id",
|
||||
in: &HostSet{
|
||||
HostSet: &store.HostSet{
|
||||
Attributes: attrs,
|
||||
},
|
||||
},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "invalid-public-id-set",
|
||||
in: &HostSet{
|
||||
HostSet: &store.HostSet{
|
||||
CatalogId: catalog.PublicId,
|
||||
PublicId: "abcd_OOOOOOOOOO",
|
||||
Attributes: attrs,
|
||||
},
|
||||
},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "invalid-no-attribte",
|
||||
in: &HostSet{
|
||||
HostSet: &store.HostSet{
|
||||
CatalogId: catalog.PublicId,
|
||||
},
|
||||
},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "valid-no-options",
|
||||
in: &HostSet{
|
||||
HostSet: &store.HostSet{
|
||||
CatalogId: catalog.PublicId,
|
||||
Attributes: attrs,
|
||||
},
|
||||
},
|
||||
want: &HostSet{
|
||||
HostSet: &store.HostSet{
|
||||
CatalogId: catalog.PublicId,
|
||||
Attributes: attrs,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-with-name",
|
||||
in: &HostSet{
|
||||
HostSet: &store.HostSet{
|
||||
CatalogId: catalog.PublicId,
|
||||
Name: "test-name-repo",
|
||||
Attributes: attrs,
|
||||
},
|
||||
},
|
||||
want: &HostSet{
|
||||
HostSet: &store.HostSet{
|
||||
CatalogId: catalog.PublicId,
|
||||
Name: "test-name-repo",
|
||||
Attributes: attrs,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-with-description",
|
||||
in: &HostSet{
|
||||
HostSet: &store.HostSet{
|
||||
CatalogId: catalog.PublicId,
|
||||
Description: ("test-description-repo"),
|
||||
Attributes: attrs,
|
||||
},
|
||||
},
|
||||
want: &HostSet{
|
||||
HostSet: &store.HostSet{
|
||||
CatalogId: catalog.PublicId,
|
||||
Description: ("test-description-repo"),
|
||||
Attributes: attrs,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
require.NoError(err)
|
||||
require.NotNil(repo)
|
||||
got, err := repo.CreateSet(context.Background(), prj.GetPublicId(), tt.in, tt.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)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Empty(tt.in.PublicId)
|
||||
require.NotNil(got)
|
||||
assert.True(strings.HasPrefix(got.GetPublicId(), HostSetPrefix))
|
||||
assert.NotSame(tt.in, got)
|
||||
assert.Equal(tt.want.Name, got.GetName())
|
||||
assert.Equal(tt.want.Description, got.GetDescription())
|
||||
assert.Equal(got.GetCreateTime(), got.GetUpdateTime())
|
||||
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)
|
||||
require.NoError(err)
|
||||
require.NotNil(repo)
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
catalog := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId)
|
||||
|
||||
in := &HostSet{
|
||||
HostSet: &store.HostSet{
|
||||
CatalogId: catalog.PublicId,
|
||||
Name: "test-name-repo",
|
||||
Attributes: []byte("{}"),
|
||||
},
|
||||
}
|
||||
|
||||
got, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in)
|
||||
require.NoError(err)
|
||||
require.NotNil(got)
|
||||
assert.True(strings.HasPrefix(got.GetPublicId(), HostSetPrefix))
|
||||
assert.NotSame(in, got)
|
||||
assert.Equal(in.Name, got.GetName())
|
||||
assert.Equal(in.Description, got.GetDescription())
|
||||
assert.Equal(got.GetCreateTime(), got.GetUpdateTime())
|
||||
|
||||
got2, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in)
|
||||
assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err)
|
||||
assert.Nil(got2)
|
||||
})
|
||||
|
||||
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)
|
||||
require.NoError(err)
|
||||
require.NotNil(repo)
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
catalogA := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId)
|
||||
catalogB := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId)
|
||||
|
||||
in := &HostSet{
|
||||
HostSet: &store.HostSet{
|
||||
Name: "test-name-repo",
|
||||
Attributes: []byte("{}"),
|
||||
},
|
||||
}
|
||||
in2 := in.clone()
|
||||
|
||||
in.CatalogId = catalogA.PublicId
|
||||
got, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in)
|
||||
require.NoError(err)
|
||||
require.NotNil(got)
|
||||
assert.True(strings.HasPrefix(got.GetPublicId(), HostSetPrefix))
|
||||
assert.NotSame(in, got)
|
||||
assert.Equal(in.Name, got.GetName())
|
||||
assert.Equal(in.Description, got.GetDescription())
|
||||
assert.Equal(got.GetCreateTime(), got.GetUpdateTime())
|
||||
|
||||
in2.CatalogId = catalogB.PublicId
|
||||
got2, err := repo.CreateSet(context.Background(), prj.GetPublicId(), in2)
|
||||
require.NoError(err)
|
||||
require.NotNil(got2)
|
||||
assert.True(strings.HasPrefix(got.GetPublicId(), HostSetPrefix))
|
||||
assert.NotSame(in2, got2)
|
||||
assert.Equal(in2.Name, got2.GetName())
|
||||
assert.Equal(in2.Description, got2.GetDescription())
|
||||
assert.Equal(got2.GetCreateTime(), got2.GetUpdateTime())
|
||||
})
|
||||
}
|
||||
|
||||
func TestRepository_LookupSet(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
plg := hostplg.TestPlugin(t, conn, "lookup", "lookup")
|
||||
|
||||
catalog := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId)
|
||||
hostSet := TestSet(t, conn, catalog.PublicId)
|
||||
hostSetId, err := newHostSetId(plg.GetIdPrefix())
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
want *HostSet
|
||||
wantIsErr errors.Code
|
||||
}{
|
||||
{
|
||||
name: "with-no-public-id",
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "with-non-existing-host-set-id",
|
||||
in: hostSetId,
|
||||
},
|
||||
{
|
||||
name: "with-existing-host-set-id",
|
||||
in: hostSet.PublicId,
|
||||
want: hostSet,
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
assert.NoError(err)
|
||||
require.NotNil(repo)
|
||||
got, err := repo.LookupSet(context.Background(), tt.in)
|
||||
if tt.wantIsErr != 0 {
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err)
|
||||
assert.Nil(got)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.EqualValues(tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_ListSets(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
plg := hostplg.TestPlugin(t, conn, "list", "list")
|
||||
catalogA := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId)
|
||||
catalogB := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId)
|
||||
|
||||
hostSets := []*HostSet{
|
||||
TestSet(t, conn, catalogA.PublicId),
|
||||
TestSet(t, conn, catalogA.PublicId),
|
||||
TestSet(t, conn, catalogA.PublicId),
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
opts []Option
|
||||
want []*HostSet
|
||||
wantIsErr errors.Code
|
||||
}{
|
||||
{
|
||||
name: "with-no-catalog-id",
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "Catalog-with-no-host-sets",
|
||||
in: catalogB.PublicId,
|
||||
want: []*HostSet{},
|
||||
},
|
||||
{
|
||||
name: "Catalog-with-host-sets",
|
||||
in: catalogA.PublicId,
|
||||
want: hostSets,
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
assert.NoError(err)
|
||||
require.NotNil(repo)
|
||||
got, err := repo.ListSets(context.Background(), tt.in, tt.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)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
opts := []cmp.Option{
|
||||
cmpopts.SortSlices(func(x, y *HostSet) bool { return x.PublicId < y.PublicId }),
|
||||
protocmp.Transform(),
|
||||
}
|
||||
assert.Empty(cmp.Diff(tt.want, got, opts...))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_ListSets_Limits(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
plg := hostplg.TestPlugin(t, conn, "listlimit", "listlimit")
|
||||
catalog := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId)
|
||||
count := 10
|
||||
var hostSets []*HostSet
|
||||
for i := 0; i < count; i++ {
|
||||
hostSets = append(hostSets, TestSet(t, conn, catalog.PublicId))
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
repoOpts []Option
|
||||
listOpts []Option
|
||||
wantLen int
|
||||
}{
|
||||
{
|
||||
name: "With no limits",
|
||||
wantLen: count,
|
||||
},
|
||||
{
|
||||
name: "With repo limit",
|
||||
repoOpts: []Option{WithLimit(3)},
|
||||
wantLen: 3,
|
||||
},
|
||||
{
|
||||
name: "With negative repo limit",
|
||||
repoOpts: []Option{WithLimit(-1)},
|
||||
wantLen: count,
|
||||
},
|
||||
{
|
||||
name: "With List limit",
|
||||
listOpts: []Option{WithLimit(3)},
|
||||
wantLen: 3,
|
||||
},
|
||||
{
|
||||
name: "With negative List limit",
|
||||
listOpts: []Option{WithLimit(-1)},
|
||||
wantLen: count,
|
||||
},
|
||||
{
|
||||
name: "With repo smaller than list limit",
|
||||
repoOpts: []Option{WithLimit(2)},
|
||||
listOpts: []Option{WithLimit(6)},
|
||||
wantLen: 6,
|
||||
},
|
||||
{
|
||||
name: "With repo larger than list limit",
|
||||
repoOpts: []Option{WithLimit(6)},
|
||||
listOpts: []Option{WithLimit(2)},
|
||||
wantLen: 2,
|
||||
},
|
||||
}
|
||||
|
||||
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, tt.repoOpts...)
|
||||
assert.NoError(err)
|
||||
require.NotNil(repo)
|
||||
got, err := repo.ListSets(context.Background(), hostSets[0].CatalogId, tt.listOpts...)
|
||||
require.NoError(err)
|
||||
assert.Len(got, tt.wantLen)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
)
|
||||
|
||||
func TestRepository_New(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
type args struct {
|
||||
r db.Reader
|
||||
w db.Writer
|
||||
kms *kms.Kms
|
||||
opts []Option
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *Repository
|
||||
wantIsErr errors.Code
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
r: rw,
|
||||
w: rw,
|
||||
kms: kmsCache,
|
||||
},
|
||||
want: &Repository{
|
||||
reader: rw,
|
||||
writer: rw,
|
||||
kms: kmsCache,
|
||||
defaultLimit: db.DefaultLimit,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-with-limit",
|
||||
args: args{
|
||||
r: rw,
|
||||
w: rw,
|
||||
kms: kmsCache,
|
||||
opts: []Option{WithLimit(5)},
|
||||
},
|
||||
want: &Repository{
|
||||
reader: rw,
|
||||
writer: rw,
|
||||
kms: kmsCache,
|
||||
defaultLimit: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil-reader",
|
||||
args: args{
|
||||
r: nil,
|
||||
w: rw,
|
||||
kms: kmsCache,
|
||||
},
|
||||
want: nil,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "nil-writer",
|
||||
args: args{
|
||||
r: rw,
|
||||
w: nil,
|
||||
kms: kmsCache,
|
||||
},
|
||||
want: nil,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "nil-kms",
|
||||
args: args{
|
||||
r: rw,
|
||||
w: rw,
|
||||
kms: nil,
|
||||
},
|
||||
want: nil,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "all-nils",
|
||||
args: args{
|
||||
r: nil,
|
||||
w: nil,
|
||||
kms: nil,
|
||||
},
|
||||
want: nil,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
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...)
|
||||
if tt.wantIsErr != 0 {
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err)
|
||||
assert.Nil(got)
|
||||
return
|
||||
}
|
||||
assert.NoError(err)
|
||||
require.NotNil(got)
|
||||
assert.Equal(tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/plugin/host"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -16,35 +17,51 @@ import (
|
||||
func TestCatalog(t *testing.T, conn *gorm.DB, pluginId, scopeId string, opt ...Option) *HostCatalog {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
w := db.New(conn)
|
||||
|
||||
cat, err := NewHostCatalog(ctx, pluginId, scopeId, opt...)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, cat)
|
||||
id, err := newHostCatalogId()
|
||||
|
||||
plg := host.NewPlugin("", "")
|
||||
plg.PublicId = pluginId
|
||||
require.NoError(t, w.LookupByPublicId(ctx, plg))
|
||||
|
||||
id, err := newHostCatalogId(plg.GetIdPrefix())
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, id)
|
||||
cat.PublicId = id
|
||||
|
||||
w := db.New(conn)
|
||||
require.NoError(t, w.Create(ctx, cat))
|
||||
return cat
|
||||
}
|
||||
|
||||
// TestSet creates count number of plugin host sets in the provided DB
|
||||
// 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, catalogId string, opt ...Option) *HostSet {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
w := db.New(conn)
|
||||
|
||||
assert := assert.New(t)
|
||||
set, err := NewHostSet(ctx, catalogId, opt...)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(set)
|
||||
id, err := newHostSetId()
|
||||
|
||||
cg := allocHostCatalog()
|
||||
cg.PublicId = catalogId
|
||||
require.NoError(t, w.LookupByPublicId(ctx, cg))
|
||||
|
||||
plg := host.NewPlugin("", "")
|
||||
plg.PublicId = cg.GetPluginId()
|
||||
require.NoError(t, w.LookupByPublicId(ctx, plg))
|
||||
|
||||
id, err := newHostSetId(plg.GetIdPrefix())
|
||||
assert.NoError(err)
|
||||
assert.NotEmpty(id)
|
||||
set.PublicId = id
|
||||
|
||||
w := db.New(conn)
|
||||
require.NoError(t, w.Create(ctx, set))
|
||||
return set
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
@@ -24,6 +26,7 @@ func Test_TestCatalogs(t *testing.T) {
|
||||
|
||||
cs := TestCatalog(t, conn, plg.GetPublicId(), proj.GetPublicId(), WithName("foo"), WithDescription("bar"))
|
||||
assert.NotEmpty(cs.GetPublicId())
|
||||
assert.True(strings.HasPrefix(cs.GetPublicId(), fmt.Sprintf("%s_%s", HostCatalogPrefix, "prefix")))
|
||||
assert.Equal("foo", cs.GetName())
|
||||
assert.Equal("bar", cs.GetDescription())
|
||||
}
|
||||
@@ -43,6 +46,7 @@ func Test_TestSet(t *testing.T) {
|
||||
c := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId())
|
||||
set := TestSet(t, conn, c.GetPublicId(), 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())
|
||||
assert.Equal("bar", set.GetDescription())
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package host
|
||||
|
||||
import "github.com/hashicorp/boundary/internal/types/subtypes"
|
||||
import (
|
||||
"github.com/hashicorp/boundary/internal/boundary"
|
||||
"github.com/hashicorp/boundary/internal/types/subtypes"
|
||||
)
|
||||
|
||||
var registry = subtypes.NewRegistry()
|
||||
|
||||
@@ -22,3 +25,22 @@ func SubtypeFromId(id string) subtypes.Subtype {
|
||||
func Register(subtype subtypes.Subtype, prefixes ...string) error {
|
||||
return registry.Register(subtype, prefixes...)
|
||||
}
|
||||
|
||||
// Catalog contains the common methods across all the different types of host catalogs.
|
||||
type Catalog interface {
|
||||
boundary.Resource
|
||||
GetScopeId() string
|
||||
}
|
||||
|
||||
// Set contains the common methods across all the different types of host sets.
|
||||
type Set interface {
|
||||
boundary.Resource
|
||||
GetCatalogId() string
|
||||
}
|
||||
|
||||
// Host contains the common methods across all the different types of hosts.
|
||||
type Host interface {
|
||||
boundary.Resource
|
||||
GetCatalogId() string
|
||||
GetAddress() string
|
||||
}
|
||||
Reference in new issue
Block a user