Read and List Plugin based Hosts (#1655)
* Add ability to read and list hosts. Tests to follow. * Add tests for reading and listing as well as validation checks to not modify hosts. * Add IpAddress and DnsNames to the api resource. Populate the plugin info. * Adding go sdk tests for reading and listing hosts and ensuring update and deleting is disallowed.
This commit is contained in:
19 files changed
+803
-107
No files matched your search
@@ -26,6 +26,8 @@ type Host struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
HostSetIds []string `json:"host_set_ids,omitempty"`
|
||||
Attributes map[string]interface{} `json:"attributes,omitempty"`
|
||||
IpAddresses []string `json:"ip_addresses,omitempty"`
|
||||
DnsNames []string `json:"dns_names,omitempty"`
|
||||
AuthorizedActions []string `json:"authorized_actions,omitempty"`
|
||||
|
||||
response *api.Response
|
||||
|
||||
@@ -62,4 +62,6 @@ const (
|
||||
PluginIdField = "plugin_id"
|
||||
PluginField = "plugin"
|
||||
PluginNameField = "plugin_name"
|
||||
IpAddressesField = "ip_addresses"
|
||||
DnsNamesField = "dns_names"
|
||||
)
|
||||
@@ -131,6 +131,12 @@ type Controller struct {
|
||||
//
|
||||
// TODO: This field is currently internal.
|
||||
StatusGracePeriodDuration time.Duration `hcl:"-"`
|
||||
|
||||
// SchedulerRunJobInterval is the time interval between waking up the
|
||||
// scheduler to run pending jobs.
|
||||
//
|
||||
// TODO: This field is currently internal.
|
||||
SchedulerRunJobInterval time.Duration `hcl:"-"`
|
||||
}
|
||||
|
||||
func (c *Controller) InitNameIfEmpty() (string, error) {
|
||||
|
||||
@@ -4213,6 +4213,22 @@
|
||||
"type": "object",
|
||||
"description": "The attributes that are applicable to the specific Host type."
|
||||
},
|
||||
"ip_addresses": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Output only. The list of ip addresses associated with this host.",
|
||||
"readOnly": true
|
||||
},
|
||||
"dns_names": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Output only. The list of dns addresses associated with this host.",
|
||||
"readOnly": true
|
||||
},
|
||||
"authorized_actions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
||||
@@ -157,7 +157,7 @@ func TestHostSetMember_InsertDelete(t *testing.T) {
|
||||
require.Len(t, hosts, 2)
|
||||
|
||||
// Base case the count by catalog ID
|
||||
hosts, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId)
|
||||
hosts, _, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, hosts, 4)
|
||||
|
||||
@@ -174,7 +174,7 @@ func TestHostSetMember_InsertDelete(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, count)
|
||||
assert.NoError(t, db.TestVerifyOplog(t, rw, blueHost1.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second)))
|
||||
hosts, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId)
|
||||
hosts, _, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, hosts, 3)
|
||||
|
||||
@@ -189,7 +189,7 @@ func TestHostSetMember_InsertDelete(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, count)
|
||||
assert.NoError(t, db.TestVerifyOplog(t, rw, blueHost2.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second)))
|
||||
hosts, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId)
|
||||
hosts, _, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, hosts, 2)
|
||||
|
||||
@@ -206,7 +206,7 @@ func TestHostSetMember_InsertDelete(t *testing.T) {
|
||||
assert.Equal(t, 2, count)
|
||||
assert.NoError(t, db.TestVerifyOplog(t, rw, blueHost3.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second)))
|
||||
assert.NoError(t, db.TestVerifyOplog(t, rw, blueHost4.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second)))
|
||||
hosts, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId)
|
||||
hosts, _, err = repo.ListHostsByCatalogId(ctx, blueCat.PublicId)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, hosts, 0)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := host.Register(Subtype, HostCatalogPrefix, HostSetPrefix); err != nil {
|
||||
if err := host.Register(Subtype, HostCatalogPrefix, HostSetPrefix, HostPrefix); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,33 +6,38 @@ import (
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
hostplugin "github.com/hashicorp/boundary/internal/plugin/host"
|
||||
)
|
||||
|
||||
// LookupHost will look up a host in the repository. If the host is not
|
||||
// found, it will return nil, nil. All options are ignored.
|
||||
func (r *Repository) LookupHost(ctx context.Context, publicId string, opt ...Option) (*Host, error) {
|
||||
func (r *Repository) LookupHost(ctx context.Context, publicId string, opt ...Option) (*Host, *hostplugin.Plugin, error) {
|
||||
const op = "plugin.(Repository).LookupHost"
|
||||
if publicId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id")
|
||||
return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no public id")
|
||||
}
|
||||
ha := &hostAgg{
|
||||
PublicId: publicId,
|
||||
}
|
||||
if err := r.reader.LookupByPublicId(ctx, ha); err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
return nil, nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", publicId)))
|
||||
return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", publicId)))
|
||||
}
|
||||
return ha.toHost(), nil
|
||||
plg, err := r.getPlugin(ctx, ha.PluginId)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
return ha.toHost(), plg, nil
|
||||
}
|
||||
|
||||
// ListHostsByCatalogId returns a slice of Hosts for the catalogId.
|
||||
// WithLimit is the only option supported.
|
||||
func (r *Repository) ListHostsByCatalogId(ctx context.Context, catalogId string, opt ...Option) ([]*Host, error) {
|
||||
func (r *Repository) ListHostsByCatalogId(ctx context.Context, catalogId string, opt ...Option) ([]*Host, *hostplugin.Plugin, error) {
|
||||
const op = "plugin.(Repository).ListHostsByCatalogId"
|
||||
if catalogId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no catalog id")
|
||||
return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no catalog id")
|
||||
}
|
||||
opts := getOpts(opt...)
|
||||
limit := r.defaultLimit
|
||||
@@ -45,9 +50,15 @@ func (r *Repository) ListHostsByCatalogId(ctx context.Context, catalogId string,
|
||||
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
case hostAggs == nil:
|
||||
return nil, nil
|
||||
return nil, nil, errors.Wrap(ctx, err, op)
|
||||
case len(hostAggs) == 0:
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
pluginId := hostAggs[0].PluginId
|
||||
plg, err := r.getPlugin(ctx, pluginId)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
hosts := make([]*Host, 0, len(hostAggs))
|
||||
@@ -55,7 +66,7 @@ func (r *Repository) ListHostsByCatalogId(ctx context.Context, catalogId string,
|
||||
hosts = append(hosts, ha.toHost())
|
||||
}
|
||||
|
||||
return hosts, nil
|
||||
return hosts, plg, nil
|
||||
}
|
||||
|
||||
// ListHostsBySetId returns a slice of Hosts for the given set IDs.
|
||||
|
||||
@@ -143,7 +143,7 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet,
|
||||
}
|
||||
|
||||
// The set now exists in the plugin, sync it immediately.
|
||||
r.scheduler.UpdateJobNextRunInAtLeast(ctx, setSyncJobName, 0)
|
||||
_ = r.scheduler.UpdateJobNextRunInAtLeast(ctx, setSyncJobName, 0)
|
||||
|
||||
plg, err := r.getPlugin(ctx, c.GetPluginId())
|
||||
if err != nil {
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
hostplg "github.com/hashicorp/boundary/internal/plugin/host"
|
||||
hostplgstore "github.com/hashicorp/boundary/internal/plugin/host/store"
|
||||
"github.com/hashicorp/boundary/internal/scheduler"
|
||||
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -225,7 +226,8 @@ func TestJob_UpsertHosts(t *testing.T) {
|
||||
require.NotNil(repo)
|
||||
|
||||
// Check again, but via performing an explicit list
|
||||
got, err = repo.ListHostsByCatalogId(ctx, in.catalog.GetPublicId())
|
||||
var gotPlg *hostplg.Plugin
|
||||
got, gotPlg, err = repo.ListHostsByCatalogId(ctx, in.catalog.GetPublicId())
|
||||
require.NoError(err)
|
||||
require.NotNil(got)
|
||||
assert.Len(got, len(in.phs))
|
||||
@@ -240,6 +242,14 @@ func TestJob_UpsertHosts(t *testing.T) {
|
||||
}),
|
||||
),
|
||||
)
|
||||
assert.Empty(
|
||||
cmp.Diff(
|
||||
plg,
|
||||
gotPlg,
|
||||
cmpopts.IgnoreUnexported(hostplg.Plugin{}, hostplgstore.Plugin{}),
|
||||
cmpopts.IgnoreTypes(×tamp.Timestamp{}),
|
||||
),
|
||||
)
|
||||
|
||||
// Now individually call read on each host, cache the matching set
|
||||
// IDs, and then check membership
|
||||
@@ -248,7 +258,7 @@ func TestJob_UpsertHosts(t *testing.T) {
|
||||
for _, setId := range exp.SetIds {
|
||||
setIdMap[setId] = append(setIdMap[setId], exp.GetPublicId())
|
||||
}
|
||||
got, err := repo.LookupHost(ctx, exp.GetPublicId())
|
||||
got, gotPlg, err := repo.LookupHost(ctx, exp.GetPublicId())
|
||||
require.NoError(err)
|
||||
require.NotNil(got)
|
||||
assert.NotEmpty(got.SetIds)
|
||||
@@ -260,6 +270,14 @@ func TestJob_UpsertHosts(t *testing.T) {
|
||||
cmpopts.IgnoreTypes(×tamp.Timestamp{}),
|
||||
),
|
||||
)
|
||||
assert.Empty(
|
||||
cmp.Diff(
|
||||
plg,
|
||||
gotPlg,
|
||||
cmpopts.IgnoreUnexported(hostplg.Plugin{}, hostplgstore.Plugin{}),
|
||||
cmpopts.IgnoreTypes(×tamp.Timestamp{}),
|
||||
),
|
||||
)
|
||||
}
|
||||
for setId, expHostIds := range setIdMap {
|
||||
got, err = repo.ListHostsBySetIds(ctx, []string{setId})
|
||||
|
||||
@@ -50,6 +50,12 @@ message Host {
|
||||
// The attributes that are applicable to the specific Host type.
|
||||
google.protobuf.Struct attributes = 110 [(custom_options.v1.generate_sdk_option) = true];
|
||||
|
||||
// Output only. The list of ip addresses associated with this host.
|
||||
repeated string ip_addresses = 120;
|
||||
|
||||
// Output only. The list of dns addresses associated with this host.
|
||||
repeated string dns_names = 130;
|
||||
|
||||
// Output only. The available actions on this resource for this user.
|
||||
repeated string authorized_actions = 300 [json_name="authorized_actions"];
|
||||
}
|
||||
|
||||
@@ -172,7 +172,11 @@ func New(ctx context.Context, conf *Config) (*Controller, error) {
|
||||
}
|
||||
// TODO: the RunJobsLimit is temporary until a better fix gets in. This
|
||||
// currently caps the scheduler at running 10 jobs per interval.
|
||||
c.scheduler, err = scheduler.New(c.conf.RawConfig.Controller.Name, jobRepoFn, scheduler.WithRunJobsLimit(10))
|
||||
schedulerOpts := []scheduler.Option{scheduler.WithRunJobsLimit(10)}
|
||||
if c.conf.RawConfig.Controller.SchedulerRunJobInterval > 0 {
|
||||
schedulerOpts = append(schedulerOpts, scheduler.WithRunJobsInterval(c.conf.RawConfig.Controller.SchedulerRunJobInterval))
|
||||
}
|
||||
c.scheduler, err = scheduler.New(c.conf.RawConfig.Controller.Name, jobRepoFn, schedulerOpts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating new scheduler: %w", err)
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ func handleGrpcGateway(c *Controller, props HandlerProperties) (http.Handler, er
|
||||
}
|
||||
}
|
||||
if _, ok := currentServices[services.HostService_ServiceDesc.ServiceName]; !ok {
|
||||
hs, err := hosts.NewService(c.StaticHostRepoFn)
|
||||
hs, err := hosts.NewService(c.StaticHostRepoFn, c.PluginHostRepoFn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create host handler service: %w", err)
|
||||
}
|
||||
|
||||
@@ -10,9 +10,11 @@ import (
|
||||
"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"
|
||||
hostplugin "github.com/hashicorp/boundary/internal/plugin/host"
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common"
|
||||
@@ -21,6 +23,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/hashicorp/boundary/internal/types/subtypes"
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hosts"
|
||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
)
|
||||
@@ -56,18 +59,22 @@ type Service struct {
|
||||
pbs.UnimplementedHostServiceServer
|
||||
|
||||
staticRepoFn common.StaticRepoFactory
|
||||
pluginRepoFn common.PluginHostRepoFactory
|
||||
}
|
||||
|
||||
var _ pbs.HostServiceServer = Service{}
|
||||
|
||||
// NewService returns a host Service which handles host related requests to boundary and uses the provided
|
||||
// repositories for storage and retrieval.
|
||||
func NewService(repoFn common.StaticRepoFactory) (Service, error) {
|
||||
func NewService(repoFn common.StaticRepoFactory, pluginRepoFn common.PluginHostRepoFactory) (Service, error) {
|
||||
const op = "hosts.NewService"
|
||||
if repoFn == 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 host repository")
|
||||
}
|
||||
return Service{staticRepoFn: repoFn, pluginRepoFn: pluginRepoFn}, nil
|
||||
}
|
||||
|
||||
func (s Service) ListHosts(ctx context.Context, req *pbs.ListHostsRequest) (*pbs.ListHostsResponse, error) {
|
||||
@@ -78,7 +85,7 @@ func (s Service) ListHosts(ctx context.Context, req *pbs.ListHostsRequest) (*pbs
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
hl, err := s.listFromRepo(ctx, req.GetHostCatalogId())
|
||||
hl, plg, err := s.listFromRepo(ctx, req.GetHostCatalogId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -107,6 +114,9 @@ func (s Service) ListHosts(ctx context.Context, req *pbs.ListHostsRequest) (*pbs
|
||||
outputFields := authResults.FetchOutputFields(res, action.List).SelfOrDefaults(authResults.UserId)
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if plg != nil {
|
||||
outputOpts = append(outputOpts, handlers.WithPlugin(plg))
|
||||
}
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
@@ -114,7 +124,12 @@ func (s Service) ListHosts(ctx context.Context, req *pbs.ListHostsRequest) (*pbs
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, item, nil, outputOpts...)
|
||||
var hostSetIds []string
|
||||
switch h := item.(type) {
|
||||
case *plugin.Host:
|
||||
hostSetIds = h.SetIds
|
||||
}
|
||||
item, err := toProto(ctx, item, hostSetIds, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -137,7 +152,7 @@ func (s Service) GetHost(ctx context.Context, req *pbs.GetHostRequest) (*pbs.Get
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
h, err := s.getFromRepo(ctx, req.GetId())
|
||||
h, plg, err := s.getFromRepo(ctx, req.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -149,6 +164,9 @@ func (s Service) GetHost(ctx context.Context, req *pbs.GetHostRequest) (*pbs.Get
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if plg != nil {
|
||||
outputOpts = append(outputOpts, handlers.WithPlugin(plg))
|
||||
}
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
@@ -156,7 +174,12 @@ func (s Service) GetHost(ctx context.Context, req *pbs.GetHostRequest) (*pbs.Get
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, h.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, h, nil, outputOpts...)
|
||||
var hostSetIds []string
|
||||
switch h := h.(type) {
|
||||
case *plugin.Host:
|
||||
hostSetIds = h.SetIds
|
||||
}
|
||||
item, err := toProto(ctx, h, hostSetIds, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -259,19 +282,38 @@ func (s Service) DeleteHost(ctx context.Context, req *pbs.DeleteHostRequest) (*p
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*static.Host, error) {
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (host.Host, *plugins.PluginInfo, error) {
|
||||
var h host.Host
|
||||
var plg *plugins.PluginInfo
|
||||
switch host.SubtypeFromId(id) {
|
||||
case static.Subtype:
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
h, err = repo.LookupHost(ctx, id)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if h == nil {
|
||||
return nil, nil, handlers.NotFoundErrorf("Host %q doesn't exist.", id)
|
||||
}
|
||||
case plugin.Subtype:
|
||||
repo, err := s.pluginRepoFn()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
ph, hPlg, err := repo.LookupHost(ctx, id)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if ph == nil {
|
||||
return nil, nil, handlers.NotFoundErrorf("Host %q doesn't exist.", id)
|
||||
}
|
||||
h = ph
|
||||
plg = toPluginInfo(hPlg)
|
||||
}
|
||||
h, err := repo.LookupHost(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if h == nil {
|
||||
return nil, handlers.NotFoundErrorf("Host %q doesn't exist.", id)
|
||||
}
|
||||
return h, nil
|
||||
return h, plg, nil
|
||||
}
|
||||
|
||||
func (s Service) createInRepo(ctx context.Context, scopeId, catalogId string, item *pb.Host) (*static.Host, error) {
|
||||
@@ -361,21 +403,47 @@ 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.Host, error) {
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (s Service) listFromRepo(ctx context.Context, catalogId string) ([]host.Host, *plugins.PluginInfo, error) {
|
||||
var hosts []host.Host
|
||||
var plg *plugins.PluginInfo
|
||||
switch host.SubtypeFromId(catalogId) {
|
||||
case static.Subtype:
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
hl, err := repo.ListHosts(ctx, catalogId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for _, h := range hl {
|
||||
hosts = append(hosts, h)
|
||||
}
|
||||
case plugin.Subtype:
|
||||
repo, err := s.pluginRepoFn()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
hl, hlPlg, err := repo.ListHostsByCatalogId(ctx, catalogId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for _, h := range hl {
|
||||
hosts = append(hosts, h)
|
||||
}
|
||||
plg = toPluginInfo(hlPlg)
|
||||
}
|
||||
hl, err := repo.ListHosts(ctx, catalogId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return hl, nil
|
||||
return hosts, plg, 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
|
||||
@@ -387,33 +455,72 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty
|
||||
case action.List, action.Create:
|
||||
parentId = id
|
||||
default:
|
||||
h, err := repo.LookupHost(ctx, id)
|
||||
switch host.SubtypeFromId(id) {
|
||||
case static.Subtype:
|
||||
h, err := staticRepo.LookupHost(ctx, id)
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return nil, res
|
||||
}
|
||||
if h == nil {
|
||||
res.Error = handlers.NotFoundError()
|
||||
return nil, res
|
||||
}
|
||||
parentId = h.GetCatalogId()
|
||||
case plugin.Subtype:
|
||||
h, _, err := pluginRepo.LookupHost(ctx, id)
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return nil, res
|
||||
}
|
||||
if h == nil {
|
||||
res.Error = handlers.NotFoundError()
|
||||
return nil, res
|
||||
}
|
||||
parentId = h.GetCatalogId()
|
||||
}
|
||||
opts = append(opts, auth.WithId(id))
|
||||
}
|
||||
|
||||
var cat host.Catalog
|
||||
switch host.SubtypeFromId(id) {
|
||||
case static.Subtype:
|
||||
cat, err = staticRepo.LookupCatalog(ctx, parentId)
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return nil, res
|
||||
}
|
||||
if h == nil {
|
||||
if cat == nil {
|
||||
res.Error = handlers.NotFoundError()
|
||||
return nil, res
|
||||
}
|
||||
case plugin.Subtype:
|
||||
cat, _, err = pluginRepo.LookupCatalog(ctx, parentId)
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return nil, res
|
||||
}
|
||||
if cat == nil {
|
||||
res.Error = handlers.NotFoundError()
|
||||
return nil, res
|
||||
}
|
||||
parentId = h.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
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(cat.GetScopeId()), auth.WithPin(parentId))
|
||||
return cat, auth.Verify(ctx, opts...)
|
||||
}
|
||||
|
||||
func toProto(ctx context.Context, in *static.Host, hostSets []*static.HostSet, opt ...handlers.Option) (*pb.Host, error) {
|
||||
func toPluginInfo(plg *hostplugin.Plugin) *plugins.PluginInfo {
|
||||
if plg == nil {
|
||||
return nil
|
||||
}
|
||||
return &plugins.PluginInfo{
|
||||
Id: plg.GetPublicId(),
|
||||
Name: plg.GetName(),
|
||||
Description: plg.GetDescription(),
|
||||
}
|
||||
}
|
||||
|
||||
func toProto(ctx context.Context, in host.Host, hostSetIds []string, opt ...handlers.Option) (*pb.Host, error) {
|
||||
opts := handlers.GetOpts(opt...)
|
||||
if opts.WithOutputFields == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building host proto")
|
||||
@@ -428,7 +535,12 @@ func toProto(ctx context.Context, in *static.Host, hostSets []*static.HostSet, o
|
||||
out.HostCatalogId = in.GetCatalogId()
|
||||
}
|
||||
if outputFields.Has(globals.TypeField) {
|
||||
out.Type = static.Subtype.String()
|
||||
switch in.(type) {
|
||||
case *static.Host:
|
||||
out.Type = static.Subtype.String()
|
||||
case *plugin.Host:
|
||||
out.Type = plugin.Subtype.String()
|
||||
}
|
||||
}
|
||||
if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" {
|
||||
out.Description = &wrapperspb.StringValue{Value: in.GetDescription()}
|
||||
@@ -443,7 +555,12 @@ func toProto(ctx context.Context, in *static.Host, hostSets []*static.HostSet, o
|
||||
out.UpdatedTime = in.GetUpdateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.VersionField) {
|
||||
out.Version = in.GetVersion()
|
||||
switch in.(type) {
|
||||
case *static.Host:
|
||||
out.Version = in.GetVersion()
|
||||
case *plugin.Host:
|
||||
out.Version = 1
|
||||
}
|
||||
}
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
out.Scope = opts.WithScope
|
||||
@@ -452,16 +569,31 @@ func toProto(ctx context.Context, in *static.Host, hostSets []*static.HostSet, o
|
||||
out.AuthorizedActions = opts.WithAuthorizedActions
|
||||
}
|
||||
if outputFields.Has(globals.HostSetIdsField) {
|
||||
for _, hs := range hostSets {
|
||||
out.HostSetIds = append(out.HostSetIds, hs.GetPublicId())
|
||||
for _, hs := range hostSetIds {
|
||||
out.HostSetIds = append(out.HostSetIds, hs)
|
||||
}
|
||||
}
|
||||
if outputFields.Has(globals.AttributesField) {
|
||||
st, err := handlers.ProtoToStruct(&pb.StaticHostAttributes{Address: wrapperspb.String(in.GetAddress())})
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to convert static attribute to struct: %s", err)
|
||||
switch h := in.(type) {
|
||||
case *static.Host:
|
||||
st, err := handlers.ProtoToStruct(&pb.StaticHostAttributes{Address: wrapperspb.String(h.GetAddress())})
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to convert static attribute to struct: %s", err)
|
||||
}
|
||||
out.Attributes = st
|
||||
}
|
||||
}
|
||||
if outputFields.Has(globals.PluginField) {
|
||||
out.Plugin = opts.WithPlugin
|
||||
}
|
||||
switch h := in.(type) {
|
||||
case *plugin.Host:
|
||||
if outputFields.Has(globals.IpAddressesField) {
|
||||
out.IpAddresses = h.IpAddresses
|
||||
}
|
||||
if outputFields.Has(globals.DnsNamesField) {
|
||||
out.DnsNames = h.DnsNames
|
||||
}
|
||||
out.Attributes = st
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
@@ -481,7 +613,7 @@ func validateGetRequest(req *pbs.GetHostRequest) error {
|
||||
badFields["id"] = "Improperly formatted identifier used."
|
||||
}
|
||||
return badFields
|
||||
}, req, static.HostPrefix)
|
||||
}, req, static.HostPrefix, plugin.HostPrefix)
|
||||
}
|
||||
|
||||
func validateCreateRequest(req *pbs.CreateHostRequest) error {
|
||||
@@ -493,11 +625,17 @@ func validateCreateRequest(req *pbs.CreateHostRequest) error {
|
||||
switch host.SubtypeFromId(req.GetItem().GetHostCatalogId()) {
|
||||
case static.Subtype:
|
||||
if req.GetItem().GetType() != "" && req.GetItem().GetType() != static.Subtype.String() {
|
||||
badFields["type"] = "Doesn't match the parent resource's type."
|
||||
badFields[globals.TypeField] = "Doesn't match the parent resource's type."
|
||||
}
|
||||
if len(req.GetItem().GetIpAddresses()) > 0 {
|
||||
badFields[globals.IpAddressesField] = "This field is not supported for this host type."
|
||||
}
|
||||
if len(req.GetItem().GetDnsNames()) > 0 {
|
||||
badFields[globals.DnsNamesField] = "This field is not supported for this host type."
|
||||
}
|
||||
attrs := &pb.StaticHostAttributes{}
|
||||
if err := handlers.StructToProto(req.GetItem().GetAttributes(), attrs); err != nil {
|
||||
badFields["attributes"] = "Attribute fields do not match the expected format."
|
||||
badFields[globals.AttributesField] = "Attribute fields do not match the expected format."
|
||||
}
|
||||
if attrs.GetAddress() == nil ||
|
||||
len(attrs.GetAddress().GetValue()) < static.MinHostAddressLength ||
|
||||
@@ -513,6 +651,8 @@ func validateCreateRequest(req *pbs.CreateHostRequest) error {
|
||||
default:
|
||||
badFields["attributes.address"] = fmt.Sprintf("Error parsing address: %v.", err)
|
||||
}
|
||||
case plugin.Subtype:
|
||||
badFields[globals.HostCatalogIdField] = "Cannot manually create hosts for this type of catalog."
|
||||
}
|
||||
return badFields
|
||||
})
|
||||
@@ -524,8 +664,7 @@ func validateUpdateRequest(req *pbs.UpdateHostRequest) error {
|
||||
switch host.SubtypeFromId(req.GetId()) {
|
||||
case static.Subtype:
|
||||
if req.GetItem().GetType() != "" && req.GetItem().GetType() != static.Subtype.String() {
|
||||
badFields["type"] = "Cannot modify the resource type."
|
||||
|
||||
badFields[globals.TypeField] = "Cannot modify the resource type."
|
||||
attrs := &pb.StaticHostAttributes{}
|
||||
if err := handlers.StructToProto(req.GetItem().GetAttributes(), attrs); err != nil {
|
||||
badFields["attributes"] = "Attribute fields do not match the expected format."
|
||||
@@ -539,6 +678,8 @@ func validateUpdateRequest(req *pbs.UpdateHostRequest) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
case plugin.Subtype:
|
||||
badFields[globals.IdField] = "Cannot modify this type of host."
|
||||
default:
|
||||
badFields["id"] = "Improperly formatted identifier used."
|
||||
}
|
||||
@@ -547,12 +688,19 @@ func validateUpdateRequest(req *pbs.UpdateHostRequest) error {
|
||||
}
|
||||
|
||||
func validateDeleteRequest(req *pbs.DeleteHostRequest) error {
|
||||
return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, static.HostPrefix)
|
||||
return handlers.ValidateDeleteRequest(func() map[string]string {
|
||||
badFields := map[string]string{}
|
||||
switch host.SubtypeFromId(req.GetId()) {
|
||||
case plugin.Subtype:
|
||||
badFields[globals.IdField] = "Cannot manually delete this type of host."
|
||||
}
|
||||
return badFields
|
||||
}, req, static.HostPrefix)
|
||||
}
|
||||
|
||||
func validateListRequest(req *pbs.ListHostsRequest) error {
|
||||
badFields := map[string]string{}
|
||||
if !handlers.ValidId(handlers.Id(req.GetHostCatalogId()), static.HostCatalogPrefix) {
|
||||
if !handlers.ValidId(handlers.Id(req.GetHostCatalogId()), static.HostCatalogPrefix, plugin.HostCatalogPrefix) {
|
||||
badFields["host_catalog_id"] = "The field is incorrectly formatted."
|
||||
}
|
||||
if _, err := handlers.NewFilter(req.GetFilter()); err != nil {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -11,15 +12,20 @@ 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"
|
||||
hostplugin "github.com/hashicorp/boundary/internal/plugin/host"
|
||||
"github.com/hashicorp/boundary/internal/scheduler"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers/hosts"
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hosts"
|
||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins"
|
||||
"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"
|
||||
@@ -28,11 +34,12 @@ import (
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
)
|
||||
|
||||
var testAuthorizedActions = []string{"no-op", "read", "update", "delete"}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
func TestGet_Static(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
@@ -46,6 +53,10 @@ func TestGet(t *testing.T) {
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
rw := db.New(conn)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
pluginRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{})
|
||||
}
|
||||
repoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
}
|
||||
@@ -98,8 +109,8 @@ func TestGet(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 := hosts.NewService(repoFn)
|
||||
require.NoError(err, "Couldn't create a new host set service.")
|
||||
s, err := hosts.NewService(repoFn, pluginRepoFn)
|
||||
require.NoError(err, "Couldn't create a new host service.")
|
||||
|
||||
got, gErr := s.GetHost(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
if tc.err != nil {
|
||||
@@ -115,7 +126,106 @@ 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)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
plg := hostplugin.TestPlugin(t, conn, "test")
|
||||
plgm := map[string]plgpb.HostPluginServiceClient{
|
||||
plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}),
|
||||
}
|
||||
|
||||
rw := db.New(conn)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
pluginRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms, sche, plgm)
|
||||
}
|
||||
repoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
}
|
||||
hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
|
||||
h := plugin.TestHost(t, conn, hc.GetPublicId(), "test")
|
||||
hs := plugin.TestSet(t, conn, kms, sche, hc, plgm)
|
||||
plugin.TestSetMembers(t, conn, hs.GetPublicId(), []*plugin.Host{h})
|
||||
|
||||
pHost := &pb.Host{
|
||||
HostCatalogId: hc.GetPublicId(),
|
||||
Id: h.GetPublicId(),
|
||||
CreatedTime: h.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: h.UpdateTime.GetTimestamp(),
|
||||
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()},
|
||||
Type: plugin.Subtype.String(),
|
||||
Plugin: &plugins.PluginInfo{
|
||||
Id: plg.GetPublicId(),
|
||||
Name: plg.GetName(),
|
||||
Description: plg.GetDescription(),
|
||||
},
|
||||
HostSetIds: []string{hs.GetPublicId()},
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
req *pbs.GetHostRequest
|
||||
res *pbs.GetHostResponse
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "Get an Existing Host",
|
||||
req: &pbs.GetHostRequest{Id: h.GetPublicId()},
|
||||
res: &pbs.GetHostResponse{Item: pHost},
|
||||
},
|
||||
{
|
||||
name: "non existing",
|
||||
req: &pbs.GetHostRequest{Id: plugin.HostPrefix + "_DoesntExis"},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.NotFound),
|
||||
},
|
||||
{
|
||||
name: "Wrong id prefix",
|
||||
req: &pbs.GetHostRequest{Id: "j_1234567890"},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "space in id",
|
||||
req: &pbs.GetHostRequest{Id: plugin.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)
|
||||
s, err := hosts.NewService(repoFn, pluginRepoFn)
|
||||
require.NoError(err, "Couldn't create a new host service.")
|
||||
|
||||
got, gErr := s.GetHost(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "GetHost(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
return
|
||||
}
|
||||
require.NoError(gErr)
|
||||
|
||||
if tc.res != nil {
|
||||
tc.res.Item.Version = 1
|
||||
}
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "GetHost(%q) got response %q, wanted %q", tc.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)
|
||||
@@ -128,6 +238,10 @@ func TestList(t *testing.T) {
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
rw := db.New(conn)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
pluginRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{})
|
||||
}
|
||||
repoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
}
|
||||
@@ -185,7 +299,7 @@ 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 := hosts.NewService(repoFn)
|
||||
s, err := hosts.NewService(repoFn, pluginRepoFn)
|
||||
require.NoError(err, "Couldn't create new host set service.")
|
||||
|
||||
// Test non-anonymous listing
|
||||
@@ -211,6 +325,124 @@ func TestList(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
plg := hostplugin.TestPlugin(t, conn, "test")
|
||||
plgm := map[string]plgpb.HostPluginServiceClient{
|
||||
plg.GetPublicId(): plugin.NewWrappingPluginClient(&plugin.TestPluginServer{}),
|
||||
}
|
||||
|
||||
rw := db.New(conn)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
pluginRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms, sche, plgm)
|
||||
}
|
||||
repoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
}
|
||||
hcs := plugin.TestCatalogs(t, conn, proj.GetPublicId(), plg.GetPublicId(), 2)
|
||||
hc, hcNoHosts := hcs[0], hcs[1]
|
||||
hs := plugin.TestSet(t, conn, kms, sche, hc, plgm)
|
||||
|
||||
var wantHs []*pb.Host
|
||||
for i := 0; i < 10; i++ {
|
||||
h := plugin.TestHost(t, conn, hc.GetPublicId(), fmt.Sprintf("host %d", i))
|
||||
plugin.TestSetMembers(t, conn, hs.GetPublicId(), []*plugin.Host{h})
|
||||
wantHs = append(wantHs, &pb.Host{
|
||||
Id: h.GetPublicId(),
|
||||
HostCatalogId: h.GetCatalogId(),
|
||||
Plugin: &plugins.PluginInfo{
|
||||
Id: plg.GetPublicId(),
|
||||
Name: plg.GetName(),
|
||||
Description: plg.GetDescription(),
|
||||
},
|
||||
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()},
|
||||
CreatedTime: h.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: h.GetUpdateTime().GetTimestamp(),
|
||||
HostSetIds: []string{hs.GetPublicId()},
|
||||
Version: 1,
|
||||
Type: plugin.Subtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
})
|
||||
}
|
||||
sort.Slice(wantHs, func(i, j int) bool {
|
||||
return wantHs[i].GetId() < wantHs[j].GetId()
|
||||
})
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
req *pbs.ListHostsRequest
|
||||
res *pbs.ListHostsResponse
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "List Many Hosts",
|
||||
req: &pbs.ListHostsRequest{HostCatalogId: hc.GetPublicId()},
|
||||
res: &pbs.ListHostsResponse{Items: wantHs},
|
||||
},
|
||||
{
|
||||
name: "List No Hosts",
|
||||
req: &pbs.ListHostsRequest{HostCatalogId: hcNoHosts.GetPublicId()},
|
||||
res: &pbs.ListHostsResponse{},
|
||||
},
|
||||
{
|
||||
name: "Filter to One Host",
|
||||
req: &pbs.ListHostsRequest{HostCatalogId: hc.GetPublicId(), Filter: fmt.Sprintf(`"/item/id"==%q`, wantHs[1].GetId())},
|
||||
res: &pbs.ListHostsResponse{Items: wantHs[1:2]},
|
||||
},
|
||||
{
|
||||
name: "Filter to No Hosts",
|
||||
req: &pbs.ListHostsRequest{HostCatalogId: hc.GetPublicId(), Filter: `"/item/id"=="doesnt match"`},
|
||||
res: &pbs.ListHostsResponse{},
|
||||
},
|
||||
{
|
||||
name: "Filter Bad Format",
|
||||
req: &pbs.ListHostsRequest{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 := hosts.NewService(repoFn, pluginRepoFn)
|
||||
require.NoError(err, "Couldn't create new host set service.")
|
||||
|
||||
// Test non-anonymous listing
|
||||
got, gErr := s.ListHosts(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListHosts(%q) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
return
|
||||
}
|
||||
require.NoError(gErr)
|
||||
|
||||
sort.Slice(got.Items, func(i, j int) bool {
|
||||
return got.Items[i].GetId() < got.Items[j].GetId()
|
||||
})
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListHosts(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
||||
|
||||
// Test anonymous listing
|
||||
got, gErr = s.ListHosts(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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
@@ -225,13 +457,21 @@ func TestDelete(t *testing.T) {
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
rw := db.New(conn)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
pluginRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{})
|
||||
}
|
||||
repoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
}
|
||||
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
||||
h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0]
|
||||
|
||||
s, err := hosts.NewService(repoFn)
|
||||
plg := hostplugin.TestPlugin(t, conn, "test")
|
||||
pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
|
||||
pluginH := plugin.TestHost(t, conn, pluginHc.GetPublicId(), "test")
|
||||
|
||||
s, err := hosts.NewService(repoFn, pluginRepoFn)
|
||||
require.NoError(t, err, "Couldn't create a new host set service.")
|
||||
|
||||
cases := []struct {
|
||||
@@ -248,6 +488,14 @@ func TestDelete(t *testing.T) {
|
||||
Id: h.GetPublicId(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Delete a plugin Host",
|
||||
scopeId: proj.GetPublicId(),
|
||||
req: &pbs.DeleteHostRequest{
|
||||
Id: pluginH.GetPublicId(),
|
||||
},
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Delete bad id Host",
|
||||
scopeId: proj.GetPublicId(),
|
||||
@@ -293,13 +541,17 @@ func TestDelete_twice(t *testing.T) {
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
rw := db.New(conn)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
pluginRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{})
|
||||
}
|
||||
repoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
}
|
||||
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
||||
h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0]
|
||||
|
||||
s, err := hosts.NewService(repoFn)
|
||||
s, err := hosts.NewService(repoFn, pluginRepoFn)
|
||||
require.NoError(err, "Couldn't create a new host set service.")
|
||||
req := &pbs.DeleteHostRequest{
|
||||
Id: h.GetPublicId(),
|
||||
@@ -326,11 +578,18 @@ func TestCreate(t *testing.T) {
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
rw := db.New(conn)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
pluginRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{})
|
||||
}
|
||||
repoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
}
|
||||
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
||||
|
||||
plg := hostplugin.TestPlugin(t, conn, "test")
|
||||
pluginHc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
|
||||
|
||||
defaultHcCreated := hc.GetCreateTime().GetTimestamp().AsTime()
|
||||
|
||||
cases := []struct {
|
||||
@@ -365,6 +624,14 @@ func TestCreate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Create a plugin Host",
|
||||
req: &pbs.CreateHostRequest{Item: &pb.Host{
|
||||
HostCatalogId: pluginHc.GetPublicId(),
|
||||
Type: "plugin",
|
||||
}},
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Create with empty address",
|
||||
req: &pbs.CreateHostRequest{Item: &pb.Host{
|
||||
@@ -454,7 +721,7 @@ func TestCreate(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 := hosts.NewService(repoFn)
|
||||
s, err := hosts.NewService(repoFn, pluginRepoFn)
|
||||
require.NoError(err, "Failed to create a new host set service.")
|
||||
|
||||
got, gErr := s.CreateHost(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
@@ -498,6 +765,10 @@ func TestUpdate(t *testing.T) {
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
rw := db.New(conn)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
pluginRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{})
|
||||
}
|
||||
repoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
}
|
||||
@@ -525,7 +796,7 @@ func TestUpdate(t *testing.T) {
|
||||
Id: h.GetPublicId(),
|
||||
}
|
||||
|
||||
tested, err := hosts.NewService(repoFn)
|
||||
tested, err := hosts.NewService(repoFn, pluginRepoFn)
|
||||
require.NoError(t, err, "Failed to create a new host set service.")
|
||||
|
||||
cases := []struct {
|
||||
@@ -887,3 +1158,42 @@ func TestUpdate(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdate_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
|
||||
}
|
||||
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
rw := db.New(conn)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
pluginRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{})
|
||||
}
|
||||
repoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
plg := hostplugin.TestPlugin(t, conn, "test")
|
||||
hc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
|
||||
h := plugin.TestHost(t, conn, hc.GetPublicId(), "test")
|
||||
|
||||
tested, err := hosts.NewService(repoFn, pluginRepoFn)
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := tested.UpdateHost(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), &pbs.UpdateHostRequest{
|
||||
Id: h.GetPublicId(),
|
||||
Item: &pb.Host{Name: wrapperspb.String("foo")},
|
||||
UpdateMask: &field_mask.FieldMask{Paths: []string{"name"}},
|
||||
})
|
||||
assert.Nil(t, got)
|
||||
require.Error(t, err)
|
||||
assert.True(t, errors.Is(err, handlers.ApiErrorWithCode(codes.InvalidArgument)))
|
||||
}
|
||||
@@ -385,6 +385,10 @@ type TestControllerOpts struct {
|
||||
// The amount of time to wait before marking connections as closed when a
|
||||
// worker has not reported in
|
||||
StatusGracePeriodDuration time.Duration
|
||||
|
||||
// The amount of time between the scheduler waking up to run it's registered
|
||||
// jobs.
|
||||
SchedulerRunJobInterval time.Duration
|
||||
}
|
||||
|
||||
func NewTestController(t *testing.T, opts *TestControllerOpts) *TestController {
|
||||
@@ -478,6 +482,7 @@ func NewTestController(t *testing.T, opts *TestControllerOpts) *TestController {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
opts.Config.Controller.SchedulerRunJobInterval = opts.SchedulerRunJobInterval
|
||||
|
||||
if err := tc.b.SetupEventing(tc.b.Logger, tc.b.StderrLock, opts.Config.Controller.Name, base.WithEventerConfig(opts.Config.Eventing)); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -59,6 +59,52 @@ func TestList(t *testing.T) {
|
||||
assert.Equal(filterItem.Id, ul.Items[0].Id)
|
||||
}
|
||||
|
||||
func TestList_Plugin(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
tc := controller.NewTestController(t, nil)
|
||||
defer tc.Shutdown()
|
||||
|
||||
client := tc.Client()
|
||||
token := tc.Token()
|
||||
client.SetToken(token.Token)
|
||||
_, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId))
|
||||
catalogClient := hostcatalogs.NewClient(client)
|
||||
|
||||
ul, err := catalogClient.List(tc.Context(), proj.GetPublicId())
|
||||
require.NoError(err)
|
||||
assert.Empty(ul.Items)
|
||||
|
||||
var expected []*hostcatalogs.HostCatalog
|
||||
for i := 0; i < 10; i++ {
|
||||
expected = append(expected, &hostcatalogs.HostCatalog{Name: fmt.Sprint(i)})
|
||||
}
|
||||
|
||||
cr, err := catalogClient.Create(tc.Context(), "plugin", proj.GetPublicId(),
|
||||
hostcatalogs.WithName(expected[0].Name), hostcatalogs.WithPluginId("pl_1234567890"))
|
||||
require.NoError(err)
|
||||
expected[0] = cr.Item
|
||||
|
||||
ul, err = catalogClient.List(tc.Context(), proj.GetPublicId())
|
||||
require.NoError(err)
|
||||
assert.ElementsMatch(comparableCatalogSlice(expected[:1]), comparableCatalogSlice(ul.Items))
|
||||
|
||||
for i := 1; i < 10; i++ {
|
||||
cr, err = catalogClient.Create(tc.Context(), "static", proj.GetPublicId(), hostcatalogs.WithName(expected[i].Name))
|
||||
require.NoError(err)
|
||||
expected[i] = cr.Item
|
||||
}
|
||||
ul, err = catalogClient.List(tc.Context(), proj.GetPublicId())
|
||||
require.NoError(err)
|
||||
assert.ElementsMatch(comparableCatalogSlice(expected), comparableCatalogSlice(ul.Items))
|
||||
|
||||
filterItem := ul.Items[3]
|
||||
ul, err = catalogClient.List(tc.Context(), proj.GetPublicId(),
|
||||
hostcatalogs.WithFilter(fmt.Sprintf(`"/item/id"==%q`, filterItem.Id)))
|
||||
require.NoError(err)
|
||||
assert.Len(ul.Items, 1)
|
||||
assert.Equal(filterItem.Id, ul.Items[0].Id)
|
||||
}
|
||||
|
||||
func comparableCatalogSlice(in []*hostcatalogs.HostCatalog) []hostcatalogs.HostCatalog {
|
||||
var filtered []hostcatalogs.HostCatalog
|
||||
for _, i := range in {
|
||||
|
||||
@@ -4,10 +4,12 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/api"
|
||||
"github.com/hashicorp/boundary/api/hostcatalogs"
|
||||
"github.com/hashicorp/boundary/api/hosts"
|
||||
"github.com/hashicorp/boundary/api/hostsets"
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller"
|
||||
@@ -80,6 +82,53 @@ func comparableHostSlice(in []*hosts.Host) []hosts.Host {
|
||||
return filtered
|
||||
}
|
||||
|
||||
func TestPluginHosts(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
tc := controller.NewTestController(t, &controller.TestControllerOpts{SchedulerRunJobInterval: 100 * time.Millisecond})
|
||||
defer tc.Shutdown()
|
||||
|
||||
client := tc.Client()
|
||||
token := tc.Token()
|
||||
client.SetToken(token.Token)
|
||||
_, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId))
|
||||
|
||||
hc, err := hostcatalogs.NewClient(client).Create(tc.Context(), "plugin", proj.GetPublicId(),
|
||||
hostcatalogs.WithPluginId("pl_1234567890"))
|
||||
require.NotNil(hc)
|
||||
|
||||
hset, err := hostsets.NewClient(client).Create(tc.Context(), hc.Item.Id, hostsets.WithAttributes(map[string]interface{}{
|
||||
"host_info": []interface{}{
|
||||
map[string]interface{}{
|
||||
"external_id": "test1",
|
||||
"ip_addresses": []string{"10.0.0.1", "192.168.1.1"},
|
||||
"dns_names": []string{"foo.hashicorp.com", "boundaryproject.io"},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"external_id": "test2",
|
||||
"ip_addresses": []string{"10.0.0.2", "192.168.1.2"},
|
||||
"dns_names": []string{"foo2.hashicorp.com", "boundaryproject2.io"},
|
||||
},
|
||||
},
|
||||
}))
|
||||
require.NoError(err)
|
||||
require.NotNil(hset)
|
||||
time.Sleep(1*time.Second)
|
||||
|
||||
hClient := hosts.NewClient(client)
|
||||
hl, err := hClient.List(tc.Context(), hc.Item.Id)
|
||||
require.NoError(err)
|
||||
require.Len(hl.Items, 2)
|
||||
|
||||
h, err := hClient.Read(tc.Context(), hl.Items[0].Id)
|
||||
require.NoError(err)
|
||||
assert.Equal(hl.Items[0], h.Item)
|
||||
|
||||
_, err = hClient.Update(tc.Context(), h.Item.Id, h.Item.Version, hosts.WithName("foo"))
|
||||
require.Error(err)
|
||||
_, err = hClient.Delete(tc.Context(), h.Item.Id)
|
||||
require.Error(err)
|
||||
}
|
||||
|
||||
func TestCrud(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
tc := controller.NewTestController(t, nil)
|
||||
|
||||
@@ -102,6 +102,57 @@ func TestList(t *testing.T) {
|
||||
assert.Equal(filterItem.Id, ul.Items[0].Id)
|
||||
}
|
||||
|
||||
func TestList_Plugin(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
tc := controller.NewTestController(t, nil)
|
||||
defer tc.Shutdown()
|
||||
|
||||
client := tc.Client()
|
||||
token := tc.Token()
|
||||
client.SetToken(token.Token)
|
||||
_, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId))
|
||||
|
||||
hc, err := hostcatalogs.NewClient(client).Create(tc.Context(), "plugin", proj.GetPublicId(),
|
||||
hostcatalogs.WithPluginId("pl_1234567890"))
|
||||
require.NoError(err)
|
||||
require.NotNil(hc)
|
||||
|
||||
hClient := hostsets.NewClient(client)
|
||||
|
||||
ul, err := hClient.List(tc.Context(), hc.Item.Id)
|
||||
require.NoError(err)
|
||||
assert.Empty(ul.Items)
|
||||
|
||||
var expected []*hostsets.HostSet
|
||||
for i := 0; i < 10; i++ {
|
||||
expected = append(expected, &hostsets.HostSet{Name: fmt.Sprint(i)})
|
||||
}
|
||||
|
||||
hcr, err := hClient.Create(tc.Context(), hc.Item.Id, hostsets.WithName(expected[0].Name))
|
||||
require.NoError(err)
|
||||
expected[0] = hcr.Item
|
||||
|
||||
ul, err = hClient.List(tc.Context(), hc.Item.Id)
|
||||
require.NoError(err)
|
||||
assert.ElementsMatch(comparableSetSlice(expected[:1]), comparableSetSlice(ul.Items))
|
||||
|
||||
for i := 1; i < 10; i++ {
|
||||
hcr, err = hClient.Create(tc.Context(), hc.Item.Id, hostsets.WithName(expected[i].Name))
|
||||
require.NoError(err)
|
||||
expected[i] = hcr.Item
|
||||
}
|
||||
ul, err = hClient.List(tc.Context(), hc.Item.Id)
|
||||
require.NoError(err)
|
||||
assert.ElementsMatch(comparableSetSlice(expected), comparableSetSlice(ul.Items))
|
||||
|
||||
filterItem := ul.Items[3]
|
||||
ul, err = hClient.List(tc.Context(), hc.Item.Id,
|
||||
hostsets.WithFilter(fmt.Sprintf(`"/item/id"==%q`, filterItem.Id)))
|
||||
require.NoError(err)
|
||||
assert.Len(ul.Items, 1)
|
||||
assert.Equal(filterItem.Id, ul.Items[0].Id)
|
||||
}
|
||||
|
||||
func comparableSetSlice(in []*hostsets.HostSet) []hostsets.HostSet {
|
||||
var filtered []hostsets.HostSet
|
||||
for _, i := range in {
|
||||
|
||||
@@ -57,6 +57,10 @@ type Host struct {
|
||||
HostSetIds []string `protobuf:"bytes,100,rep,name=host_set_ids,proto3" json:"host_set_ids,omitempty"`
|
||||
// The attributes that are applicable to the specific Host type.
|
||||
Attributes *structpb.Struct `protobuf:"bytes,110,opt,name=attributes,proto3" json:"attributes,omitempty"`
|
||||
// Output only. The list of ip addresses associated with this host.
|
||||
IpAddresses []string `protobuf:"bytes,120,rep,name=ip_addresses,json=ipAddresses,proto3" json:"ip_addresses,omitempty"`
|
||||
// Output only. The list of dns addresses associated with this host.
|
||||
DnsNames []string `protobuf:"bytes,130,rep,name=dns_names,json=dnsNames,proto3" json:"dns_names,omitempty"`
|
||||
// Output only. The available actions on this resource for this user.
|
||||
AuthorizedActions []string `protobuf:"bytes,300,rep,name=authorized_actions,proto3" json:"authorized_actions,omitempty"`
|
||||
}
|
||||
@@ -177,6 +181,20 @@ func (x *Host) GetAttributes() *structpb.Struct {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Host) GetIpAddresses() []string {
|
||||
if x != nil {
|
||||
return x.IpAddresses
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Host) GetDnsNames() []string {
|
||||
if x != nil {
|
||||
return x.DnsNames
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Host) GetAuthorizedActions() []string {
|
||||
if x != nil {
|
||||
return x.AuthorizedActions
|
||||
@@ -254,7 +272,7 @@ var file_controller_api_resources_hosts_v1_host_proto_rawDesc = []byte{
|
||||
0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63,
|
||||
0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31,
|
||||
0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbc,
|
||||
0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xfd,
|
||||
0x05, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f,
|
||||
0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09,
|
||||
@@ -295,23 +313,27 @@ var file_controller_api_resources_hosts_v1_host_proto_rawDesc = []byte{
|
||||
0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x6e, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x04, 0xa0, 0xda, 0x29, 0x01,
|
||||
0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12,
|
||||
0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f,
|
||||
0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x75, 0x0a,
|
||||
0x14, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69,
|
||||
0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x5d, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x42, 0x25, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1d, 0x0a, 0x12,
|
||||
0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x61, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x12, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75,
|
||||
0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f,
|
||||
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x3b, 0x68, 0x6f, 0x73,
|
||||
0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c,
|
||||
0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x78, 0x20, 0x03,
|
||||
0x28, 0x09, 0x52, 0x0b, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12,
|
||||
0x1c, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x82, 0x01, 0x20,
|
||||
0x03, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x2f, 0x0a,
|
||||
0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68,
|
||||
0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x75,
|
||||
0x0a, 0x14, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72,
|
||||
0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x5d, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67,
|
||||
0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x25, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1d, 0x0a,
|
||||
0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x61, 0x64, 0x64, 0x72,
|
||||
0x65, 0x73, 0x73, 0x12, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
|
||||
0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f,
|
||||
0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63,
|
||||
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x3b, 0x68, 0x6f,
|
||||
0x73, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
Reference in new issue
Block a user