Create Plugin Host Set Handler for Create, List, Get (#1518)
This commit is contained in:
1 parent
e208800f77
commit
715982148f
5 files changed
+734
-102
No files matched your search
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/auth/oidc"
|
||||
"github.com/hashicorp/boundary/internal/auth/password"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault"
|
||||
pluginhost "github.com/hashicorp/boundary/internal/host/plugin"
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/servers"
|
||||
@@ -19,6 +20,7 @@ type (
|
||||
PasswordAuthRepoFactory func() (*password.Repository, error)
|
||||
ServersRepoFactory func() (*servers.Repository, error)
|
||||
StaticRepoFactory func() (*static.Repository, error)
|
||||
PluginHostRepoFactory func() (*pluginhost.Repository, error)
|
||||
SessionRepoFactory func() (*session.Repository, error)
|
||||
TargetRepoFactory func() (*target.Repository, error)
|
||||
)
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/cmd/config"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/host/plugin"
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
@@ -53,6 +54,7 @@ type Controller struct {
|
||||
ServersRepoFn common.ServersRepoFactory
|
||||
SessionRepoFn common.SessionRepoFactory
|
||||
StaticHostRepoFn common.StaticRepoFactory
|
||||
PluginHostRepoFn common.PluginHostRepoFactory
|
||||
TargetRepoFn common.TargetRepoFactory
|
||||
|
||||
scheduler *scheduler.Scheduler
|
||||
@@ -131,6 +133,9 @@ func New(ctx context.Context, conf *Config) (*Controller, error) {
|
||||
c.StaticHostRepoFn = func() (*static.Repository, error) {
|
||||
return static.NewRepository(dbase, dbase, c.kms)
|
||||
}
|
||||
c.PluginHostRepoFn = func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(dbase, dbase, c.kms)
|
||||
}
|
||||
c.AuthTokenRepoFn = func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(dbase, dbase, c.kms,
|
||||
authtoken.WithTokenTimeToLiveDuration(c.conf.RawConfig.Controller.AuthTokenTimeToLiveDuration),
|
||||
|
||||
@@ -92,7 +92,7 @@ func handleGrpcGateway(c *Controller, props HandlerProperties) (http.Handler, er
|
||||
if err := services.RegisterHostCatalogServiceHandlerServer(ctx, mux, hcs); err != nil {
|
||||
return nil, fmt.Errorf("failed to register host catalog service handler: %w", err)
|
||||
}
|
||||
hss, err := host_sets.NewService(c.StaticHostRepoFn)
|
||||
hss, err := host_sets.NewService(c.StaticHostRepoFn, c.PluginHostRepoFn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create host set handler service: %w", err)
|
||||
}
|
||||
|
||||
@@ -2,12 +2,15 @@ package host_sets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/host"
|
||||
"github.com/hashicorp/boundary/internal/host/plugin"
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/host/static/store"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
@@ -20,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/types/known/structpb"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
)
|
||||
|
||||
@@ -57,18 +61,22 @@ type Service struct {
|
||||
pbs.UnimplementedHostSetServiceServer
|
||||
|
||||
staticRepoFn common.StaticRepoFactory
|
||||
pluginRepoFn common.PluginHostRepoFactory
|
||||
}
|
||||
|
||||
var _ pbs.HostSetServiceServer = Service{}
|
||||
|
||||
// NewService returns a host set Service which handles host set related requests to boundary and uses the provided
|
||||
// repositories for storage and retrieval.
|
||||
func NewService(repoFn common.StaticRepoFactory) (Service, error) {
|
||||
func NewService(staticRepoFn common.StaticRepoFactory, pluginRepoFn common.PluginHostRepoFactory) (Service, error) {
|
||||
const op = "host_sets.NewService"
|
||||
if repoFn == nil {
|
||||
if staticRepoFn == nil {
|
||||
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing static repository")
|
||||
}
|
||||
return Service{staticRepoFn: repoFn}, nil
|
||||
if pluginRepoFn == nil {
|
||||
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing plugin repository")
|
||||
}
|
||||
return Service{staticRepoFn: staticRepoFn, pluginRepoFn: pluginRepoFn}, nil
|
||||
}
|
||||
|
||||
func (s Service) ListHostSets(ctx context.Context, req *pbs.ListHostSetsRequest) (*pbs.ListHostSetsResponse, error) {
|
||||
@@ -217,7 +225,7 @@ func (s Service) UpdateHostSet(ctx context.Context, req *pbs.UpdateHostSetReques
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
hs, hosts, err := s.updateInRepo(ctx, authResults.Scope.GetId(), cat.GetPublicId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
|
||||
hs, hosts, err := s.updateInRepo(ctx, authResults.Scope.GetId(), cat.GetPublicId(), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -373,63 +381,96 @@ func (s Service) RemoveHostSetHosts(ctx context.Context, req *pbs.RemoveHostSetH
|
||||
return &pbs.RemoveHostSetHostsResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*static.HostSet, []*static.Host, error) {
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (host.Set, []host.Host, error) {
|
||||
var hs host.Set
|
||||
var hl []host.Host
|
||||
switch host.SubtypeFromId(id) {
|
||||
case static.Subtype:
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
hset, hosts, err := repo.LookupSet(ctx, id)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if hset == nil {
|
||||
return nil, nil, handlers.NotFoundErrorf("Host Set %q doesn't exist.", id)
|
||||
}
|
||||
for _, h := range hosts {
|
||||
hl = append(hl, h)
|
||||
}
|
||||
hs = hset
|
||||
case plugin.Subtype:
|
||||
repo, err := s.pluginRepoFn()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
hset, err := repo.LookupSet(ctx, id)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if hset == nil {
|
||||
return nil, nil, handlers.NotFoundErrorf("Host Set %q doesn't exist.", id)
|
||||
}
|
||||
hs = hset
|
||||
}
|
||||
h, m, err := repo.LookupSet(ctx, id)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if h == nil {
|
||||
return nil, nil, handlers.NotFoundErrorf("Host Set %q doesn't exist.", id)
|
||||
}
|
||||
return h, m, nil
|
||||
return hs, hl, nil
|
||||
}
|
||||
|
||||
func (s Service) createInRepo(ctx context.Context, scopeId, catalogId string, item *pb.HostSet) (*static.HostSet, error) {
|
||||
func (s Service) createInRepo(ctx context.Context, scopeId, catalogId string, item *pb.HostSet) (host.Set, error) {
|
||||
const op = "host_sets.(Service).createInRepo"
|
||||
var opts []static.Option
|
||||
if item.GetName() != nil {
|
||||
opts = append(opts, static.WithName(item.GetName().GetValue()))
|
||||
if item == nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing item")
|
||||
}
|
||||
if item.GetDescription() != nil {
|
||||
opts = append(opts, static.WithDescription(item.GetDescription().GetValue()))
|
||||
var hSet host.Set
|
||||
switch host.SubtypeFromId(catalogId) {
|
||||
case static.Subtype:
|
||||
h, err := toStorageStaticSet(ctx, catalogId, item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := repo.CreateSet(ctx, scopeId, h)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create host set"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create host set but no error returned from repository.")
|
||||
}
|
||||
hSet = out
|
||||
case plugin.Subtype:
|
||||
h, err := toStoragePluginSet(ctx, catalogId, item)
|
||||
repo, err := s.pluginRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := repo.CreateSet(ctx, scopeId, h)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create host set"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create host set but no error returned from repository.")
|
||||
}
|
||||
hSet = out
|
||||
default:
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "unrecognized catalog type")
|
||||
}
|
||||
h, err := static.NewHostSet(catalogId, opts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("Unable to build host set for creation"))
|
||||
}
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := repo.CreateSet(ctx, scopeId, h)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create host set"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create host set but no error returned from repository.")
|
||||
}
|
||||
return out, nil
|
||||
return hSet, nil
|
||||
}
|
||||
|
||||
func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId, id string, mask []string, item *pb.HostSet) (*static.HostSet, []*static.Host, error) {
|
||||
func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId string, req *pbs.UpdateHostSetRequest) (host.Set, []host.Host, error) {
|
||||
const op = "host_sets.(Service).updateInRepo"
|
||||
var opts []static.Option
|
||||
if desc := item.GetDescription(); desc != nil {
|
||||
opts = append(opts, static.WithDescription(desc.GetValue()))
|
||||
}
|
||||
if name := item.GetName(); name != nil {
|
||||
opts = append(opts, static.WithName(name.GetValue()))
|
||||
}
|
||||
h, err := static.NewHostSet(catalogId, opts...)
|
||||
item := req.GetItem()
|
||||
h, err := toStorageStaticSet(ctx, catalogId, item)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("Unable to build host set for update"))
|
||||
}
|
||||
h.PublicId = id
|
||||
dbMask := maskManager.Translate(mask)
|
||||
h.PublicId = req.GetId()
|
||||
dbMask := maskManager.Translate(req.GetUpdateMask().GetPaths())
|
||||
if len(dbMask) == 0 {
|
||||
return nil, nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."})
|
||||
}
|
||||
@@ -442,9 +483,13 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId, id string
|
||||
return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update host set"))
|
||||
}
|
||||
if rowsUpdated == 0 {
|
||||
return nil, nil, handlers.NotFoundErrorf("Host Set %q doesn't exist or incorrect version provided.", id)
|
||||
return nil, nil, handlers.NotFoundErrorf("Host Set %q doesn't exist or incorrect version provided.", req.GetId())
|
||||
}
|
||||
return out, m, nil
|
||||
var hl []host.Host
|
||||
for _, h := range m {
|
||||
hl = append(hl, h)
|
||||
}
|
||||
return out, hl, nil
|
||||
}
|
||||
|
||||
func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, error) {
|
||||
@@ -460,19 +505,39 @@ func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool,
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, catalogId string) ([]*static.HostSet, error) {
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (s Service) listFromRepo(ctx context.Context, catalogId string) ([]host.Set, error) {
|
||||
const op = "host_sets.(Service).listFromRepo"
|
||||
var sets []host.Set
|
||||
switch host.SubtypeFromId(catalogId) {
|
||||
case static.Subtype:
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sl, err := repo.ListSets(ctx, catalogId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
for _, a := range sl {
|
||||
sets = append(sets, a)
|
||||
}
|
||||
case plugin.Subtype:
|
||||
repo, err := s.pluginRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sl, err := repo.ListSets(ctx, catalogId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
for _, a := range sl {
|
||||
sets = append(sets, a)
|
||||
}
|
||||
}
|
||||
hl, err := repo.ListSets(ctx, catalogId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return hl, nil
|
||||
return sets, nil
|
||||
}
|
||||
|
||||
func (s Service) addInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*static.HostSet, []*static.Host, error) {
|
||||
func (s Service) addInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*static.HostSet, []host.Host, error) {
|
||||
const op = "host_sets.(Service).addInRepo"
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
@@ -489,10 +554,14 @@ func (s Service) addInRepo(ctx context.Context, scopeId, setId string, hostIds [
|
||||
if out == nil {
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup host set after adding hosts to it.")
|
||||
}
|
||||
return out, m, nil
|
||||
var hl []host.Host
|
||||
for _, h := range m {
|
||||
hl = append(hl, h)
|
||||
}
|
||||
return out, hl, nil
|
||||
}
|
||||
|
||||
func (s Service) setInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*static.HostSet, []*static.Host, error) {
|
||||
func (s Service) setInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*static.HostSet, []host.Host, error) {
|
||||
const op = "host_sets.(Service).setInRepo"
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
@@ -510,10 +579,14 @@ func (s Service) setInRepo(ctx context.Context, scopeId, setId string, hostIds [
|
||||
if out == nil {
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup host set after setting hosts for it.")
|
||||
}
|
||||
return out, m, nil
|
||||
var hl []host.Host
|
||||
for _, h := range m {
|
||||
hl = append(hl, h)
|
||||
}
|
||||
return out, hl, nil
|
||||
}
|
||||
|
||||
func (s Service) removeInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*static.HostSet, []*static.Host, error) {
|
||||
func (s Service) removeInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*static.HostSet, []host.Host, error) {
|
||||
const op = "host_sets.(Service).removeInRepo"
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
@@ -530,12 +603,22 @@ func (s Service) removeInRepo(ctx context.Context, scopeId, setId string, hostId
|
||||
if out == nil {
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup host set after removing hosts from it.")
|
||||
}
|
||||
return out, m, nil
|
||||
var hl []host.Host
|
||||
for _, h := range m {
|
||||
hl = append(hl, h)
|
||||
}
|
||||
return out, hl, nil
|
||||
}
|
||||
|
||||
func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type) (*static.HostCatalog, auth.VerifyResults) {
|
||||
func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type) (host.Catalog, auth.VerifyResults) {
|
||||
res := auth.VerifyResults{}
|
||||
repo, err := s.staticRepoFn()
|
||||
|
||||
staticRepo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return nil, res
|
||||
}
|
||||
pluginRepo, err := s.pluginRepoFn()
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return nil, res
|
||||
@@ -547,33 +630,66 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty
|
||||
case action.List, action.Create:
|
||||
parentId = id
|
||||
default:
|
||||
set, _, err := repo.LookupSet(ctx, id)
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return nil, res
|
||||
}
|
||||
if set == nil {
|
||||
res.Error = handlers.NotFoundError()
|
||||
return nil, res
|
||||
var set host.Set
|
||||
switch host.SubtypeFromId(id) {
|
||||
case static.Subtype:
|
||||
ss, _, err := staticRepo.LookupSet(ctx, id)
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return nil, res
|
||||
}
|
||||
if ss == nil {
|
||||
res.Error = handlers.NotFoundError()
|
||||
return nil, res
|
||||
}
|
||||
set = ss
|
||||
case plugin.Subtype:
|
||||
ps, err := pluginRepo.LookupSet(ctx, id)
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return nil, res
|
||||
}
|
||||
if ps == nil {
|
||||
res.Error = handlers.NotFoundError()
|
||||
return nil, res
|
||||
}
|
||||
set = ps
|
||||
}
|
||||
parentId = set.GetCatalogId()
|
||||
opts = append(opts, auth.WithId(id))
|
||||
}
|
||||
|
||||
cat, err := repo.LookupCatalog(ctx, parentId)
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return nil, res
|
||||
}
|
||||
if cat == nil {
|
||||
res.Error = handlers.NotFoundError()
|
||||
return nil, res
|
||||
var cat host.Catalog
|
||||
switch host.SubtypeFromId(id) {
|
||||
case static.Subtype:
|
||||
cs, err := staticRepo.LookupCatalog(ctx, parentId)
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return nil, res
|
||||
}
|
||||
if cs == nil {
|
||||
res.Error = handlers.NotFoundError()
|
||||
return nil, res
|
||||
}
|
||||
cat = cs
|
||||
case plugin.Subtype:
|
||||
pc, err := pluginRepo.LookupCatalog(ctx, parentId)
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return nil, res
|
||||
}
|
||||
if pc == nil {
|
||||
res.Error = handlers.NotFoundError()
|
||||
return nil, res
|
||||
}
|
||||
cat = pc
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(cat.GetScopeId()), auth.WithPin(parentId))
|
||||
return cat, auth.Verify(ctx, opts...)
|
||||
}
|
||||
|
||||
func toProto(ctx context.Context, in *static.HostSet, hosts []*static.Host, opt ...handlers.Option) (*pb.HostSet, error) {
|
||||
func toProto(ctx context.Context, in host.Set, hosts []host.Host, opt ...handlers.Option) (*pb.HostSet, error) {
|
||||
const op = "host_set_service.toProto"
|
||||
opts := handlers.GetOpts(opt...)
|
||||
if opts.WithOutputFields == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building hostset proto")
|
||||
@@ -588,7 +704,16 @@ func toProto(ctx context.Context, in *static.HostSet, hosts []*static.Host, opt
|
||||
out.HostCatalogId = in.GetCatalogId()
|
||||
}
|
||||
if outputFields.Has(globals.TypeField) {
|
||||
out.Type = static.Subtype.String()
|
||||
switch host.SubtypeFromId(in.GetPublicId()) {
|
||||
case static.Subtype:
|
||||
out.Type = static.Subtype.String()
|
||||
case plugin.Subtype:
|
||||
out.Type = "plugin"
|
||||
idParts := strings.Split(in.GetPublicId(), "_")
|
||||
if len(idParts) > 2 {
|
||||
out.Type = idParts[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" {
|
||||
out.Description = &wrapperspb.StringValue{Value: in.GetDescription()}
|
||||
@@ -616,9 +741,62 @@ func toProto(ctx context.Context, in *static.HostSet, hosts []*static.Host, opt
|
||||
out.HostIds = append(out.HostIds, h.GetPublicId())
|
||||
}
|
||||
}
|
||||
|
||||
if outputFields.Has(globals.AttributesField) {
|
||||
switch h := in.(type) {
|
||||
case *plugin.HostSet:
|
||||
attrs := map[string]interface{}{}
|
||||
err := json.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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
func toStorageStaticSet(ctx context.Context, catalogId string, item *pb.HostSet) (*static.HostSet, error) {
|
||||
const op = "host_set_service.toStorageStaticSet"
|
||||
var opts []static.Option
|
||||
if item.GetName() != nil {
|
||||
opts = append(opts, static.WithName(item.GetName().GetValue()))
|
||||
}
|
||||
if item.GetDescription() != nil {
|
||||
opts = append(opts, static.WithDescription(item.GetDescription().GetValue()))
|
||||
}
|
||||
hs, err := static.NewHostSet(catalogId, opts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("Unable to build host set for creation"))
|
||||
}
|
||||
return hs, nil
|
||||
}
|
||||
|
||||
func toStoragePluginSet(ctx context.Context, catalogId string, item *pb.HostSet) (*plugin.HostSet, error) {
|
||||
const op = "host_set_service.toStoragePluginSet"
|
||||
var opts []plugin.Option
|
||||
if item.GetName() != nil {
|
||||
opts = append(opts, plugin.WithName(item.GetName().GetValue()))
|
||||
}
|
||||
if item.GetDescription() != nil {
|
||||
opts = append(opts, plugin.WithDescription(item.GetDescription().GetValue()))
|
||||
}
|
||||
if item.GetAttributes() != nil {
|
||||
opts = append(opts, plugin.WithAttributes(item.GetAttributes().AsMap()))
|
||||
}
|
||||
hs, err := plugin.NewHostSet(ctx, catalogId, opts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("Unable to build host set for creation"))
|
||||
}
|
||||
return hs, nil
|
||||
}
|
||||
|
||||
// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce
|
||||
// requirements on the structure of the request. They verify that:
|
||||
// * The path passed in is correctly formatted
|
||||
@@ -627,13 +805,25 @@ func toProto(ctx context.Context, in *static.HostSet, hosts []*static.Host, opt
|
||||
// * The type asserted by the ID and/or field is known
|
||||
// * If relevant, the type derived from the id prefix matches what is claimed by the type field
|
||||
func validateGetRequest(req *pbs.GetHostSetRequest) error {
|
||||
return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, static.HostSetPrefix)
|
||||
pluginPrefix := plugin.HostSetPrefix
|
||||
idParts := strings.Split(req.GetId(), "_")
|
||||
if len(idParts) > 2 && idParts[0] == plugin.HostSetPrefix {
|
||||
pluginPrefix = strings.Join(idParts[:2], "_")
|
||||
}
|
||||
|
||||
return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, static.HostSetPrefix, pluginPrefix)
|
||||
}
|
||||
|
||||
func validateCreateRequest(req *pbs.CreateHostSetRequest) error {
|
||||
return handlers.ValidateCreateRequest(req.GetItem(), func() map[string]string {
|
||||
badFields := map[string]string{}
|
||||
if !handlers.ValidId(handlers.Id(req.GetItem().GetHostCatalogId()), static.HostCatalogPrefix) {
|
||||
pluginPrefix := plugin.HostCatalogPrefix
|
||||
idParts := strings.Split(req.GetItem().GetHostCatalogId(), "_")
|
||||
if len(idParts) > 2 && idParts[0] == plugin.HostCatalogPrefix {
|
||||
pluginPrefix = strings.Join(idParts[:2], "_")
|
||||
}
|
||||
|
||||
if !handlers.ValidId(handlers.Id(req.GetItem().GetHostCatalogId()), static.HostCatalogPrefix, pluginPrefix) {
|
||||
badFields["host_catalog_id"] = "The field is incorrectly formatted."
|
||||
}
|
||||
switch host.SubtypeFromId(req.GetItem().GetHostCatalogId()) {
|
||||
@@ -641,6 +831,20 @@ func validateCreateRequest(req *pbs.CreateHostSetRequest) error {
|
||||
if req.GetItem().GetType() != "" && req.GetItem().GetType() != static.Subtype.String() {
|
||||
badFields["type"] = "Doesn't match the parent resource's type."
|
||||
}
|
||||
case plugin.Subtype:
|
||||
// TODO: Remove this check when plugin id prefixes can differ from plugin name
|
||||
if req.GetItem().GetType() == "" {
|
||||
break
|
||||
}
|
||||
idParts := strings.Split(req.GetItem().GetHostCatalogId(), "_")
|
||||
if len(idParts) < 2 {
|
||||
badFields["host_catalog_id"] = "The field is incorrectly formatted for a plugin id."
|
||||
break
|
||||
}
|
||||
plgType := idParts[1]
|
||||
if plgType != req.GetItem().GetType() {
|
||||
badFields["type"] = "This type must match the type of the host catalog."
|
||||
}
|
||||
}
|
||||
return badFields
|
||||
})
|
||||
@@ -664,8 +868,14 @@ func validateDeleteRequest(req *pbs.DeleteHostSetRequest) error {
|
||||
}
|
||||
|
||||
func validateListRequest(req *pbs.ListHostSetsRequest) error {
|
||||
pluginPrefix := plugin.HostCatalogPrefix
|
||||
idParts := strings.Split(req.GetHostCatalogId(), "_")
|
||||
if len(idParts) > 2 && idParts[0] == plugin.HostCatalogPrefix {
|
||||
pluginPrefix = strings.Join(idParts[:2], "_")
|
||||
}
|
||||
|
||||
badFields := map[string]string{}
|
||||
if !handlers.ValidId(handlers.Id(req.GetHostCatalogId()), static.HostCatalogPrefix) {
|
||||
if !handlers.ValidId(handlers.Id(req.GetHostCatalogId()), static.HostCatalogPrefix, pluginPrefix) {
|
||||
badFields["host_catalog_id"] = "The field is incorrectly formatted."
|
||||
}
|
||||
if _, err := handlers.NewFilter(req.GetFilter()); err != nil {
|
||||
|
||||
@@ -11,9 +11,11 @@ import (
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/host/plugin"
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/plugin/host"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers/host_sets"
|
||||
@@ -27,12 +29,13 @@ import (
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
var testAuthorizedActions = []string{"no-op", "read", "update", "delete", "add-hosts", "set-hosts", "remove-hosts"}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
func TestGet_Static(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
@@ -49,6 +52,9 @@ func TestGet(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)
|
||||
}
|
||||
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
||||
hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
|
||||
|
||||
@@ -105,7 +111,7 @@ func TestGet(t *testing.T) {
|
||||
req := proto.Clone(toMerge).(*pbs.GetHostSetRequest)
|
||||
proto.Merge(req, tc.req)
|
||||
|
||||
s, err := host_sets.NewService(repoFn)
|
||||
s, err := host_sets.NewService(repoFn, pluginRepoFn)
|
||||
require.NoError(err, "Couldn't create a new host set service.")
|
||||
|
||||
got, gErr := s.GetHostSet(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req)
|
||||
@@ -122,7 +128,8 @@ func TestGet(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
func TestGet_Plugin(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
@@ -138,6 +145,101 @@ func TestList(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 := host.TestPlugin(t, conn, name, name)
|
||||
hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
|
||||
hs := plugin.TestSet(t, conn, hc.GetPublicId())
|
||||
|
||||
toMerge := &pbs.GetHostSetRequest{}
|
||||
|
||||
pHost := &pb.HostSet{
|
||||
HostCatalogId: hc.GetPublicId(),
|
||||
Id: hs.GetPublicId(),
|
||||
CreatedTime: hs.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: hs.UpdateTime.GetTimestamp(),
|
||||
Type: name,
|
||||
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()},
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
req *pbs.GetHostSetRequest
|
||||
res *pbs.GetHostSetResponse
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "Get an Existing Host",
|
||||
req: &pbs.GetHostSetRequest{Id: hs.GetPublicId()},
|
||||
res: &pbs.GetHostSetResponse{Item: pHost},
|
||||
},
|
||||
{
|
||||
name: "Get a non existing Host Set",
|
||||
req: &pbs.GetHostSetRequest{Id: static.HostSetPrefix + "_DoesntExis"},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.NotFound),
|
||||
},
|
||||
{
|
||||
name: "Wrong id prefix",
|
||||
req: &pbs.GetHostSetRequest{Id: "j_1234567890"},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "space in id",
|
||||
req: &pbs.GetHostSetRequest{Id: static.HostPrefix + "_1 23456789"},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
req := proto.Clone(toMerge).(*pbs.GetHostSetRequest)
|
||||
proto.Merge(req, tc.req)
|
||||
|
||||
s, err := host_sets.NewService(repoFn, pluginRepoFn)
|
||||
require.NoError(err, "Couldn't create a new host set service.")
|
||||
|
||||
got, gErr := s.GetHostSet(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "GetHostSet(%+v) got error %v, wanted %v", req, gErr, tc.err)
|
||||
return
|
||||
}
|
||||
require.NoError(gErr)
|
||||
|
||||
if tc.res != nil {
|
||||
tc.res.Item.Version = 1
|
||||
}
|
||||
assert.Empty(cmp.Diff(got.GetItem(), tc.res.GetItem(), protocmp.Transform()), "GetHostSet(%q) got response %q, wanted %q", req, got, tc.res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestList_Static(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
rw := db.New(conn)
|
||||
repoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
}
|
||||
pluginRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms)
|
||||
}
|
||||
hcs := static.TestCatalogs(t, conn, proj.GetPublicId(), 2)
|
||||
hc, hcNoHosts := hcs[0], hcs[1]
|
||||
|
||||
@@ -190,7 +292,108 @@ func TestList(t *testing.T) {
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
s, err := host_sets.NewService(repoFn)
|
||||
s, err := host_sets.NewService(repoFn, pluginRepoFn)
|
||||
require.NoError(err, "Couldn't create new host set service.")
|
||||
|
||||
// Test with non-anon user
|
||||
got, gErr := s.ListHostSets(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListHostSets(%q) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
return
|
||||
}
|
||||
require.NoError(gErr)
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListHostSets(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
||||
|
||||
// Test with anon user
|
||||
got, gErr = s.ListHostSets(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId(), auth.WithUserId(auth.AnonymousUserId)), tc.req)
|
||||
require.NoError(gErr)
|
||||
assert.Len(got.Items, len(tc.res.Items))
|
||||
for _, item := range got.GetItems() {
|
||||
require.Nil(item.CreatedTime)
|
||||
require.Nil(item.UpdatedTime)
|
||||
require.Zero(item.Version)
|
||||
require.Empty(item.HostIds)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestList_Plugin(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
rw := db.New(conn)
|
||||
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 := host.TestPlugin(t, conn, name, name)
|
||||
hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
|
||||
hcNoHosts := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
|
||||
|
||||
var wantHs []*pb.HostSet
|
||||
for i := 0; i < 10; i++ {
|
||||
h := plugin.TestSet(t, conn, hc.GetPublicId())
|
||||
wantHs = append(wantHs, &pb.HostSet{
|
||||
Id: h.GetPublicId(),
|
||||
HostCatalogId: h.GetCatalogId(),
|
||||
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()},
|
||||
CreatedTime: h.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: h.GetUpdateTime().GetTimestamp(),
|
||||
Version: h.GetVersion(),
|
||||
Type: name,
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
})
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
req *pbs.ListHostSetsRequest
|
||||
res *pbs.ListHostSetsResponse
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "List Many Host Sets",
|
||||
req: &pbs.ListHostSetsRequest{HostCatalogId: hc.GetPublicId()},
|
||||
res: &pbs.ListHostSetsResponse{Items: wantHs},
|
||||
},
|
||||
{
|
||||
name: "List No Host Sets",
|
||||
req: &pbs.ListHostSetsRequest{HostCatalogId: hcNoHosts.GetPublicId()},
|
||||
res: &pbs.ListHostSetsResponse{},
|
||||
},
|
||||
{
|
||||
name: "Filter To One Host Set",
|
||||
req: &pbs.ListHostSetsRequest{HostCatalogId: hc.GetPublicId(), Filter: fmt.Sprintf(`"/item/id"==%q`, wantHs[1].GetId())},
|
||||
res: &pbs.ListHostSetsResponse{Items: wantHs[1:2]},
|
||||
},
|
||||
{
|
||||
name: "Filter To No Host Sets",
|
||||
req: &pbs.ListHostSetsRequest{HostCatalogId: hc.GetPublicId(), Filter: `"/item/name"=="doesnt match"`},
|
||||
res: &pbs.ListHostSetsResponse{},
|
||||
},
|
||||
{
|
||||
name: "Filter Bad Format",
|
||||
req: &pbs.ListHostSetsRequest{HostCatalogId: hc.GetPublicId(), Filter: `"//id/"=="bad"`},
|
||||
err: handlers.InvalidArgumentErrorf("bad format", nil),
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
s, err := host_sets.NewService(repoFn, pluginRepoFn)
|
||||
require.NoError(err, "Couldn't create new host set service.")
|
||||
|
||||
// Test with non-anon user
|
||||
@@ -234,10 +437,13 @@ func TestDelete(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)
|
||||
}
|
||||
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
||||
h := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
|
||||
|
||||
s, err := host_sets.NewService(repoFn)
|
||||
s, err := host_sets.NewService(repoFn, pluginRepoFn)
|
||||
require.NoError(t, err, "Couldn't create a new host set service.")
|
||||
|
||||
cases := []struct {
|
||||
@@ -310,10 +516,13 @@ func TestDelete_twice(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)
|
||||
}
|
||||
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
||||
h := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
|
||||
|
||||
s, err := host_sets.NewService(repoFn)
|
||||
s, err := host_sets.NewService(repoFn, plgRepoFn)
|
||||
require.NoError(err, "Couldn't create a new host set service.")
|
||||
req := &pbs.DeleteHostSetRequest{
|
||||
Id: h.GetPublicId(),
|
||||
@@ -326,7 +535,7 @@ func TestDelete_twice(t *testing.T) {
|
||||
assert.True(errors.Is(gErr, handlers.ApiErrorWithCode(codes.NotFound)), "Expected permission denied for the second delete.")
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
func TestCreate_Static(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
@@ -343,6 +552,9 @@ func TestCreate(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)
|
||||
}
|
||||
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
||||
|
||||
defaultHcCreated := hc.GetCreateTime().GetTimestamp().AsTime()
|
||||
@@ -434,7 +646,7 @@ func TestCreate(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
s, err := host_sets.NewService(repoFn)
|
||||
s, err := host_sets.NewService(repoFn, plgRepoFn)
|
||||
require.NoError(err, "Failed to create a new host set service.")
|
||||
|
||||
got, gErr := s.CreateHostSet(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
@@ -466,6 +678,197 @@ func TestCreate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate_Plugin(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
rw := db.New(conn)
|
||||
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 := host.TestPlugin(t, conn, name, name)
|
||||
hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
|
||||
|
||||
testAttrs, err := structpb.NewStruct(map[string]interface{}{
|
||||
"int": 1,
|
||||
"zero int": 0,
|
||||
"string": "foo",
|
||||
"zero string": "",
|
||||
"bytes": []byte("bar"),
|
||||
"zero bytes": nil,
|
||||
"bool": true,
|
||||
"zero bool": false,
|
||||
"nested": map[string]interface{}{
|
||||
"int": 1,
|
||||
"zero int": 0,
|
||||
"string": "foo",
|
||||
"zero string": "",
|
||||
"bytes": []byte("bar"),
|
||||
"zero bytes": nil,
|
||||
"bool": true,
|
||||
"zero bool": false,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
defaultHcCreated := hc.GetCreateTime().GetTimestamp().AsTime()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
req *pbs.CreateHostSetRequest
|
||||
res *pbs.CreateHostSetResponse
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "No Attributes",
|
||||
req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{
|
||||
HostCatalogId: hc.GetPublicId(),
|
||||
Name: &wrappers.StringValue{Value: "No Attributes"},
|
||||
Description: &wrappers.StringValue{Value: "desc"},
|
||||
Type: name,
|
||||
}},
|
||||
res: &pbs.CreateHostSetResponse{
|
||||
Uri: fmt.Sprintf("host-sets/%s_%s_", plugin.HostSetPrefix, name),
|
||||
Item: &pb.HostSet{
|
||||
HostCatalogId: hc.GetPublicId(),
|
||||
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()},
|
||||
Name: &wrappers.StringValue{Value: "No Attributes"},
|
||||
Description: &wrappers.StringValue{Value: "desc"},
|
||||
Type: name,
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "With Attributes",
|
||||
req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{
|
||||
HostCatalogId: hc.GetPublicId(),
|
||||
Name: &wrappers.StringValue{Value: "With Attributes"},
|
||||
Description: &wrappers.StringValue{Value: "desc"},
|
||||
Type: name,
|
||||
Attributes: testAttrs,
|
||||
}},
|
||||
res: &pbs.CreateHostSetResponse{
|
||||
Uri: fmt.Sprintf("host-sets/%s_%s_", plugin.HostSetPrefix, name),
|
||||
Item: &pb.HostSet{
|
||||
HostCatalogId: hc.GetPublicId(),
|
||||
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()},
|
||||
Name: &wrappers.StringValue{Value: "With Attributes"},
|
||||
Description: &wrappers.StringValue{Value: "desc"},
|
||||
Type: name,
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Attributes: testAttrs,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Create with mismatched type/name",
|
||||
req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{
|
||||
HostCatalogId: hc.GetPublicId(),
|
||||
Name: &wrappers.StringValue{Value: "name"},
|
||||
Description: &wrappers.StringValue{Value: "desc"},
|
||||
Type: "ThisIsMadeUp",
|
||||
}},
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Create with no type",
|
||||
req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{
|
||||
HostCatalogId: hc.GetPublicId(),
|
||||
Name: &wrappers.StringValue{Value: "no type name"},
|
||||
Description: &wrappers.StringValue{Value: "no type desc"},
|
||||
}},
|
||||
res: &pbs.CreateHostSetResponse{
|
||||
Uri: fmt.Sprintf("host-sets/%s_%s_", plugin.HostSetPrefix, name),
|
||||
Item: &pb.HostSet{
|
||||
HostCatalogId: hc.GetPublicId(),
|
||||
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()},
|
||||
Name: &wrappers.StringValue{Value: "no type name"},
|
||||
Description: &wrappers.StringValue{Value: "no type desc"},
|
||||
Type: name,
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Can't specify Id",
|
||||
req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{
|
||||
HostCatalogId: hc.GetPublicId(),
|
||||
Id: "not allowed to be set",
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Can't specify Created Time",
|
||||
req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{
|
||||
HostCatalogId: hc.GetPublicId(),
|
||||
CreatedTime: timestamppb.Now(),
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Can't specify Update Time",
|
||||
req: &pbs.CreateHostSetRequest{Item: &pb.HostSet{
|
||||
HostCatalogId: hc.GetPublicId(),
|
||||
UpdatedTime: timestamppb.Now(),
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
s, err := host_sets.NewService(repoFn, plgRepoFn)
|
||||
require.NoError(err, "Failed to create a new host set service.")
|
||||
|
||||
got, gErr := s.CreateHostSet(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "CreateHostSet(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
return
|
||||
}
|
||||
require.NoError(gErr)
|
||||
if got != nil {
|
||||
assert.Contains(got.GetUri(), tc.res.GetUri())
|
||||
assert.True(strings.HasPrefix(got.GetItem().GetId(), plugin.HostSetPrefix), got.GetItem().GetId())
|
||||
gotCreateTime := got.GetItem().GetCreatedTime().AsTime()
|
||||
require.NoError(err, "Error converting proto to timestamp.")
|
||||
gotUpdateTime := got.GetItem().GetUpdatedTime().AsTime()
|
||||
require.NoError(err, "Error converting proto to timestamp")
|
||||
// Verify it is a set created after the test setup's default set
|
||||
assert.True(gotCreateTime.After(defaultHcCreated), "New set should have been created after default set. Was created %v, which is after %v", gotCreateTime, defaultHcCreated)
|
||||
assert.True(gotUpdateTime.After(defaultHcCreated), "New set should have been updated after default set. Was updated %v, which is after %v", gotUpdateTime, defaultHcCreated)
|
||||
|
||||
// Clear all values which are hard to compare against.
|
||||
got.Uri, tc.res.Uri = "", ""
|
||||
got.Item.Id, tc.res.Item.Id = "", ""
|
||||
got.Item.CreatedTime, got.Item.UpdatedTime, tc.res.Item.CreatedTime, tc.res.Item.UpdatedTime = nil, nil, nil, nil
|
||||
}
|
||||
if tc.res != nil {
|
||||
tc.res.Item.Version = 1
|
||||
}
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "CreateHostSet(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
@@ -512,7 +915,10 @@ func TestUpdate(t *testing.T) {
|
||||
Id: hs.GetPublicId(),
|
||||
}
|
||||
|
||||
tested, err := host_sets.NewService(repoFn)
|
||||
plgRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms)
|
||||
}
|
||||
tested, err := host_sets.NewService(repoFn, plgRepoFn)
|
||||
require.NoError(t, err, "Failed to create a new host set service.")
|
||||
|
||||
cases := []struct {
|
||||
@@ -848,7 +1254,10 @@ func TestAddHostSetHosts(t *testing.T) {
|
||||
repoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
}
|
||||
s, err := host_sets.NewService(repoFn)
|
||||
plgRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms)
|
||||
}
|
||||
s, err := host_sets.NewService(repoFn, plgRepoFn)
|
||||
require.NoError(t, err, "Error when getting new host set service.")
|
||||
|
||||
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
||||
@@ -965,7 +1374,10 @@ func TestSetHostSetHosts(t *testing.T) {
|
||||
repoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
}
|
||||
s, err := host_sets.NewService(repoFn)
|
||||
plgRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms)
|
||||
}
|
||||
s, err := host_sets.NewService(repoFn, plgRepoFn)
|
||||
require.NoError(t, err, "Error when getting new host set service.")
|
||||
|
||||
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
||||
@@ -1078,7 +1490,10 @@ func TestRemoveHostSetHosts(t *testing.T) {
|
||||
repoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
}
|
||||
s, err := host_sets.NewService(repoFn)
|
||||
plgRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms)
|
||||
}
|
||||
s, err := host_sets.NewService(repoFn, plgRepoFn)
|
||||
require.NoError(t, err, "Error when getting new host set service.")
|
||||
|
||||
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
||||
|
||||
Reference in new issue
Block a user