internal/handlers: add host catalog pagination

pull/4202/head
Johan Brandhorst-Satzkorn 2 years ago
parent c0c28c63bc
commit f38c79a5ce

@ -11,6 +11,7 @@ import (
"github.com/hashicorp/boundary/internal/credential"
credstatic "github.com/hashicorp/boundary/internal/credential/static"
"github.com/hashicorp/boundary/internal/credential/vault"
"github.com/hashicorp/boundary/internal/host"
pluginhost "github.com/hashicorp/boundary/internal/host/plugin"
"github.com/hashicorp/boundary/internal/host/static"
"github.com/hashicorp/boundary/internal/iam"
@ -25,6 +26,7 @@ type (
VaultCredentialRepoFactory = func() (*vault.Repository, error)
StaticCredentialRepoFactory = func() (*credstatic.Repository, error)
CredentialStoreRepoFactory func() (*credential.StoreRepository, error)
HostCatalogRepoFactory func() (*host.CatalogRepository, error)
IamRepoFactory = iam.IamRepoFactory
OidcAuthRepoFactory = oidc.OidcRepoFactory
LdapAuthRepoFactory = ldap.RepoFactory

@ -30,6 +30,7 @@ import (
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/event"
intglobals "github.com/hashicorp/boundary/internal/globals"
"github.com/hashicorp/boundary/internal/host"
pluginhost "github.com/hashicorp/boundary/internal/host/plugin"
"github.com/hashicorp/boundary/internal/host/static"
"github.com/hashicorp/boundary/internal/iam"
@ -131,6 +132,7 @@ type Controller struct {
VaultCredentialRepoFn common.VaultCredentialRepoFactory
StaticCredentialRepoFn common.StaticCredentialRepoFactory
CredentialStoreRepoFn common.CredentialStoreRepoFactory
HostCatalogRepoFn common.HostCatalogRepoFactory
IamRepoFn common.IamRepoFactory
OidcRepoFn common.OidcAuthRepoFactory
LdapRepoFn common.LdapAuthRepoFactory
@ -412,6 +414,9 @@ func New(ctx context.Context, conf *Config) (*Controller, error) {
c.CredentialStoreRepoFn = func() (*credential.StoreRepository, error) {
return credential.NewStoreRepository(ctx, dbase, dbase)
}
c.HostCatalogRepoFn = func() (*host.CatalogRepository, error) {
return host.NewCatalogRepository(ctx, dbase, dbase)
}
c.ServersRepoFn = func() (*server.Repository, error) {
return server.NewRepository(ctx, dbase, dbase, c.kms)
}

@ -136,7 +136,15 @@ func (c *Controller) registerGrpcServices(s *grpc.Server) error {
currentServices := s.GetServiceInfo()
if _, ok := currentServices[services.HostCatalogService_ServiceDesc.ServiceName]; !ok {
hcs, err := host_catalogs.NewService(c.baseContext, c.StaticHostRepoFn, c.PluginHostRepoFn, c.PluginRepoFn, c.IamRepoFn)
hcs, err := host_catalogs.NewService(
c.baseContext,
c.StaticHostRepoFn,
c.PluginHostRepoFn,
c.PluginRepoFn,
c.IamRepoFn,
c.HostCatalogRepoFn,
c.conf.RawConfig.Controller.MaxPageSize,
)
if err != nil {
return fmt.Errorf("failed to create host catalog handler service: %w", err)
}

@ -21,6 +21,8 @@ import (
pluginstore "github.com/hashicorp/boundary/internal/host/plugin/store"
"github.com/hashicorp/boundary/internal/host/static"
"github.com/hashicorp/boundary/internal/host/static/store"
"github.com/hashicorp/boundary/internal/listtoken"
"github.com/hashicorp/boundary/internal/pagination"
"github.com/hashicorp/boundary/internal/perms"
"github.com/hashicorp/boundary/internal/plugin"
"github.com/hashicorp/boundary/internal/requests"
@ -30,6 +32,7 @@ import (
"github.com/hashicorp/boundary/internal/types/subtypes"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
"github.com/mr-tron/base58"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/proto"
@ -97,17 +100,27 @@ func init() {
type Service struct {
pbs.UnsafeHostCatalogServiceServer
staticRepoFn common.StaticRepoFactory
pluginHostRepoFn common.PluginHostRepoFactory
pluginRepoFn common.PluginRepoFactory
iamRepoFn common.IamRepoFactory
staticRepoFn common.StaticRepoFactory
pluginHostRepoFn common.PluginHostRepoFactory
pluginRepoFn common.PluginRepoFactory
iamRepoFn common.IamRepoFactory
hostCatalogRepoFn common.HostCatalogRepoFactory
maxPageSize uint
}
var _ pbs.HostCatalogServiceServer = (*Service)(nil)
// NewService returns a host catalog Service which handles host catalog related requests to boundary and uses the provided
// repositories for storage and retrieval.
func NewService(ctx context.Context, repoFn common.StaticRepoFactory, pluginHostRepoFn common.PluginHostRepoFactory, hostPluginRepoFn common.PluginRepoFactory, iamRepoFn common.IamRepoFactory) (Service, error) {
func NewService(
ctx context.Context,
repoFn common.StaticRepoFactory,
pluginHostRepoFn common.PluginHostRepoFactory,
hostPluginRepoFn common.PluginRepoFactory,
iamRepoFn common.IamRepoFactory,
hostCatalogRepoFn common.HostCatalogRepoFactory,
maxPageSize uint,
) (Service, error) {
const op = "host_catalogs.NewService"
if repoFn == nil {
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing static repository")
@ -121,12 +134,26 @@ func NewService(ctx context.Context, repoFn common.StaticRepoFactory, pluginHost
if iamRepoFn == nil {
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing iam repository")
}
return Service{staticRepoFn: repoFn, pluginHostRepoFn: pluginHostRepoFn, pluginRepoFn: hostPluginRepoFn, iamRepoFn: iamRepoFn}, nil
if hostCatalogRepoFn == nil {
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing host catalog repo")
}
if maxPageSize == 0 {
maxPageSize = uint(globals.DefaultMaxPageSize)
}
return Service{
staticRepoFn: repoFn,
pluginHostRepoFn: pluginHostRepoFn,
pluginRepoFn: hostPluginRepoFn,
iamRepoFn: iamRepoFn,
hostCatalogRepoFn: hostCatalogRepoFn,
maxPageSize: maxPageSize,
}, nil
}
func (s Service) ListHostCatalogs(ctx context.Context, req *pbs.ListHostCatalogsRequest) (*pbs.ListHostCatalogsResponse, error) {
const op = "host_catalogs.(Service).ListHostCatalogs"
if err := validateListRequest(ctx, req); err != nil {
return nil, err
return nil, errors.Wrap(ctx, err, op)
}
authResults := s.authResult(ctx, req.GetScopeId(), action.List)
if authResults.Error != nil {
@ -145,85 +172,140 @@ func (s Service) ListHostCatalogs(ctx context.Context, req *pbs.ListHostCatalogs
scopeIds, scopeInfoMap, err := scopeids.GetListingScopeIds(
ctx, s.iamRepoFn, authResults, req.GetScopeId(), resource.HostCatalog, req.GetRecursive())
if err != nil {
return nil, err
return nil, errors.Wrap(ctx, err, op)
}
// If no scopes match, return an empty response
if len(scopeIds) == 0 {
return &pbs.ListHostCatalogsResponse{}, nil
}
items, pluginInfoMap, err := s.listFromRepo(ctx, scopeIds)
if err != nil {
return nil, err
}
if len(items) == 0 {
return &pbs.ListHostCatalogsResponse{}, nil
pageSize := int(s.maxPageSize)
// Use the requested page size only if it is smaller than
// the configured max.
if req.GetPageSize() != 0 && uint(req.GetPageSize()) < s.maxPageSize {
pageSize = int(req.GetPageSize())
}
filter, err := handlers.NewFilter(ctx, req.GetFilter())
var filterItemFn func(ctx context.Context, item host.Catalog, plgs map[string]*plugin.Plugin) (bool, error)
switch {
case req.GetFilter() != "":
// Only use a filter if we need to
filter, err := handlers.NewFilter(ctx, req.GetFilter())
if err != nil {
return nil, err
}
// TODO: replace the need for this function with some way to convert the `filter`
// to a domain type. This would allow filtering to happen in the domain, and we could
// remove this callback altogether.
filterItemFn = func(ctx context.Context, item host.Catalog, plgs map[string]*plugin.Plugin) (bool, error) {
outputOpts, ok, err := newOutputOpts(ctx, item, authResults, scopeInfoMap, plgs)
if err != nil {
return false, err
}
if !ok {
return false, nil
}
pbItem, err := toProto(ctx, item, outputOpts...)
if err != nil {
return false, err
}
// This comes last so that we can use item fields in the filter after
// the allowed fields are populated above
filterable, err := subtypes.Filterable(ctx, pbItem)
if err != nil {
return false, err
}
return filter.Match(filterable), nil
}
default:
filterItemFn = func(ctx context.Context, item host.Catalog, plgs map[string]*plugin.Plugin) (bool, error) {
return true, nil
}
}
repo, err := s.hostCatalogRepoFn()
if err != nil {
return nil, err
return nil, errors.Wrap(ctx, err, op)
}
finalItems := make([]*pb.HostCatalog, 0, len(items))
res := perms.Resource{
Type: resource.HostCatalog,
grantsHash, err := authResults.GrantsHash(ctx)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
for _, item := range items {
res.Id = item.GetPublicId()
res.ScopeId = item.GetProjectId()
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings()
if len(authorizedActions) == 0 {
continue
}
outputFields := authResults.FetchOutputFields(res, action.List).SelfOrDefaults(authResults.UserId)
outputOpts := make([]handlers.Option, 0, 3)
outputOpts = append(outputOpts, handlers.WithOutputFields(outputFields))
if outputFields.Has(globals.ScopeField) {
outputOpts = append(outputOpts, handlers.WithScope(scopeInfoMap[item.GetProjectId()]))
var listResp *pagination.ListResponse[host.Catalog]
var plgs map[string]*plugin.Plugin
var sortBy string
if req.GetListToken() == "" {
sortBy = "created_time"
listResp, plgs, err = host.ListCatalogs(ctx, grantsHash, pageSize, filterItemFn, repo, scopeIds)
if err != nil {
return nil, err
}
if outputFields.Has(globals.AuthorizedActionsField) {
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
} else {
listToken, err := handlers.ParseListToken(ctx, req.GetListToken(), resource.HostCatalog, grantsHash)
if err != nil {
return nil, err
}
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
var subtype globals.Subtype
switch item.(type) {
case *static.HostCatalog:
subtype = static.Subtype
case *hostplugin.HostCatalog:
subtype = hostplugin.Subtype
switch st := listToken.Subtype.(type) {
case *listtoken.PaginationToken:
sortBy = "created_time"
listResp, plgs, err = host.ListCatalogsPage(ctx, grantsHash, pageSize, filterItemFn, listToken, repo, scopeIds)
if err != nil {
return nil, err
}
if subtype != "" {
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap[subtype], authResults.Scope.Id, item.GetPublicId())
if err != nil {
return nil, err
}
outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions))
case *listtoken.StartRefreshToken:
sortBy = "updated_time"
listResp, plgs, err = host.ListCatalogsRefresh(ctx, grantsHash, pageSize, filterItemFn, listToken, repo, scopeIds)
if err != nil {
return nil, err
}
}
switch hc := item.(type) {
case *hostplugin.HostCatalog:
if plgInfo, ok := pluginInfoMap[hc.GetPluginId()]; ok {
outputOpts = append(outputOpts, handlers.WithPlugin(plgInfo))
case *listtoken.RefreshToken:
sortBy = "updated_time"
listResp, plgs, err = host.ListCatalogsRefreshPage(ctx, grantsHash, pageSize, filterItemFn, listToken, repo, scopeIds)
if err != nil {
return nil, err
}
default:
return nil, handlers.ApiErrorWithCodeAndMessage(codes.InvalidArgument, "unexpected list token subtype: %T", st)
}
}
finalItems := make([]*pb.HostCatalog, 0, len(listResp.Items))
for _, item := range listResp.Items {
outputOpts, ok, err := newOutputOpts(ctx, item, authResults, scopeInfoMap, plgs)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if !ok {
continue
}
item, err := toProto(ctx, item, outputOpts...)
if err != nil {
return nil, err
return nil, errors.Wrap(ctx, err, op)
}
finalItems = append(finalItems, item)
}
respType := "delta"
if listResp.CompleteListing {
respType = "complete"
}
resp := &pbs.ListHostCatalogsResponse{
Items: finalItems,
EstItemCount: uint32(listResp.EstimatedItemCount),
RemovedIds: listResp.DeletedIds,
ResponseType: respType,
SortBy: sortBy,
SortDir: "desc",
}
// This comes last so that we can use item fields in the filter after
// the allowed fields are populated above
filterable, err := subtypes.Filterable(ctx, item)
if listResp.ListToken != nil {
resp.ListToken, err = handlers.MarshalListToken(ctx, listResp.ListToken, pbs.ResourceType_RESOURCE_TYPE_HOST_CATALOG)
if err != nil {
return nil, err
}
if filter.Match(filterable) {
finalItems = append(finalItems, item)
}
}
return &pbs.ListHostCatalogsResponse{Items: finalItems}, nil
return resp, nil
}
// GetHostCatalog implements the interface pbs.HostCatalogServiceServer.
@ -450,38 +532,6 @@ func (s Service) getFromRepo(ctx context.Context, id string) (host.Catalog, *plu
return cat, plg, nil
}
func (s Service) listFromRepo(ctx context.Context, projectIds []string) ([]host.Catalog, map[string]*plugins.PluginInfo, error) {
repo, err := s.staticRepoFn()
if err != nil {
return nil, nil, err
}
ul, err := repo.ListCatalogs(ctx, projectIds, static.WithLimit(-1))
if err != nil {
return nil, nil, err
}
var res []host.Catalog
for _, c := range ul {
res = append(res, c)
}
pluginRepo, err := s.pluginHostRepoFn()
if err != nil {
return nil, nil, err
}
pl, plgs, err := pluginRepo.ListCatalogs(ctx, projectIds, host.WithLimit(-1))
if err != nil {
return nil, nil, err
}
for _, c := range pl {
res = append(res, c)
}
pluginsMap := make(map[string]*plugins.PluginInfo, len(plgs))
for _, plg := range plgs {
pluginsMap[plg.GetPublicId()] = toPluginInfo(plg)
}
return res, pluginsMap, nil
}
func (s Service) createStaticInRepo(ctx context.Context, projId string, item *pb.HostCatalog) (*static.HostCatalog, error) {
const op = "host_catalogs.(Service).createStaticInRepo"
h, err := toStorageStaticCatalog(ctx, projId, item)
@ -714,6 +764,59 @@ func toPluginInfo(plg *plugin.Plugin) *plugins.PluginInfo {
}
}
func newOutputOpts(
ctx context.Context,
item host.Catalog,
authResults auth.VerifyResults,
scopeInfoMap map[string]*scopes.ScopeInfo,
pluginMap map[string]*plugin.Plugin,
) ([]handlers.Option, bool, error) {
res := perms.Resource{
Type: resource.HostCatalog,
Id: item.GetPublicId(),
ScopeId: item.GetProjectId(),
}
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings()
if len(authorizedActions) == 0 {
return nil, false, nil
}
outputFields := authResults.FetchOutputFields(res, action.List).SelfOrDefaults(authResults.UserId)
outputOpts := make([]handlers.Option, 0, 3)
outputOpts = append(outputOpts, handlers.WithOutputFields(outputFields))
if outputFields.Has(globals.ScopeField) {
outputOpts = append(outputOpts, handlers.WithScope(scopeInfoMap[item.GetProjectId()]))
}
if outputFields.Has(globals.AuthorizedActionsField) {
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
}
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
var subtype globals.Subtype
switch item.(type) {
case *static.HostCatalog:
subtype = static.Subtype
case *hostplugin.HostCatalog:
subtype = hostplugin.Subtype
}
if subtype != "" {
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap[subtype], authResults.Scope.Id, item.GetPublicId())
if err != nil {
return nil, false, err
}
outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions))
}
}
if pluginMap != nil {
if hc, ok := item.(*hostplugin.HostCatalog); ok {
if plg, ok := pluginMap[hc.GetPluginId()]; ok {
outputOpts = append(outputOpts, handlers.WithPlugin(toPluginInfo(plg)))
}
}
}
return outputOpts, true, nil
}
func toProto(ctx context.Context, in host.Catalog, opt ...handlers.Option) (*pb.HostCatalog, error) {
const op = "host_catalog_service.toProto"
opts := handlers.GetOpts(opt...)

@ -7,7 +7,7 @@ import (
"context"
"errors"
"fmt"
"sort"
"slices"
"strings"
"testing"
@ -15,18 +15,25 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/host_catalogs"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
authpb "github.com/hashicorp/boundary/internal/gen/controller/auth"
"github.com/hashicorp/boundary/internal/host"
hostplugin "github.com/hashicorp/boundary/internal/host/plugin"
pstore "github.com/hashicorp/boundary/internal/host/plugin/store"
"github.com/hashicorp/boundary/internal/host/static"
sstore "github.com/hashicorp/boundary/internal/host/static/store"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/plugin"
"github.com/hashicorp/boundary/internal/plugin/loopback"
"github.com/hashicorp/boundary/internal/requests"
"github.com/hashicorp/boundary/internal/scheduler"
"github.com/hashicorp/boundary/internal/server"
"github.com/hashicorp/boundary/internal/types/scope"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins"
@ -77,6 +84,41 @@ var authorizedCollectionActions = map[globals.Subtype]map[string]*structpb.ListV
var testAuthorizedActions = []string{"no-op", "read", "update", "delete"}
func pluginCatalogToProto(hc *hostplugin.HostCatalog, plg *plugin.Plugin, project *iam.Scope) *pb.HostCatalog {
return &pb.HostCatalog{
Id: hc.GetPublicId(),
ScopeId: hc.GetProjectId(),
CreatedTime: hc.GetCreateTime().GetTimestamp(),
UpdatedTime: hc.GetUpdateTime().GetTimestamp(),
Scope: &scopepb.ScopeInfo{Id: project.GetPublicId(), Type: scope.Project.String(), ParentScopeId: project.GetParentId()},
PluginId: plg.GetPublicId(),
Plugin: &plugins.PluginInfo{
Id: plg.GetPublicId(),
Name: plg.GetName(),
Description: plg.GetDescription(),
},
Version: 1,
Type: hostplugin.Subtype.String(),
SecretsHmac: base58.Encode(hc.SecretsHmac),
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: authorizedCollectionActions[hostplugin.Subtype],
}
}
func staticCatalogToProto(hc *static.HostCatalog, project *iam.Scope) *pb.HostCatalog {
return &pb.HostCatalog{
Id: hc.GetPublicId(),
ScopeId: hc.GetProjectId(),
Scope: &scopepb.ScopeInfo{Id: project.GetPublicId(), Type: scope.Project.String(), ParentScopeId: project.GetParentId()},
CreatedTime: hc.CreateTime.GetTimestamp(),
UpdatedTime: hc.UpdateTime.GetTimestamp(),
Version: 1,
Type: "static",
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: authorizedCollectionActions[static.Subtype],
}
}
func TestGet_Static(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
@ -86,34 +128,28 @@ func TestGet_Static(t *testing.T) {
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
rw := db.New(conn)
repo := func() (*static.Repository, error) {
staticRepoFn := func() (*static.Repository, error) {
return static.NewRepository(ctx, rw, rw, kms)
}
pluginHostRepo := func() (*hostplugin.Repository, error) {
pluginHostRepoFn := func() (*hostplugin.Repository, error) {
return hostplugin.NewRepository(ctx, rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{})
}
pluginRepo := func() (*plugin.Repository, error) {
pluginRepoFn := func() (*plugin.Repository, error) {
return plugin.NewRepository(ctx, rw, rw, kms)
}
iamRepoFn := func() (*iam.Repository, error) {
return iam.TestRepo(t, conn, wrapper), nil
}
catalogServiceFn := func() (*host.CatalogRepository, error) {
return host.NewCatalogRepository(ctx, rw, rw)
}
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
toMerge := &pbs.GetHostCatalogRequest{
Id: hc.GetPublicId(),
}
pHostCatalog := &pb.HostCatalog{
Id: hc.GetPublicId(),
ScopeId: hc.GetProjectId(),
Scope: &scopepb.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: proj.GetParentId()},
CreatedTime: hc.CreateTime.GetTimestamp(),
UpdatedTime: hc.UpdateTime.GetTimestamp(),
Type: "static",
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: authorizedCollectionActions[static.Subtype],
}
pHostCatalog := staticCatalogToProto(hc, proj)
cases := []struct {
name string
@ -151,7 +187,7 @@ func TestGet_Static(t *testing.T) {
req := proto.Clone(toMerge).(*pbs.GetHostCatalogRequest)
proto.Merge(req, tc.req)
s, err := host_catalogs.NewService(ctx, repo, pluginHostRepo, pluginRepo, iamRepoFn)
s, err := host_catalogs.NewService(ctx, staticRepoFn, pluginHostRepoFn, pluginRepoFn, iamRepoFn, catalogServiceFn, 1000)
require.NoError(err, "Couldn't create a new host catalog service.")
got, gErr := s.GetHostCatalog(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req)
@ -199,6 +235,9 @@ func TestGet_Plugin(t *testing.T) {
iamRepoFn := func() (*iam.Repository, error) {
return iam.TestRepo(t, conn, wrapper), nil
}
catalogServiceFn := func() (*host.CatalogRepository, error) {
return host.NewCatalogRepository(ctx, rw, rw)
}
name := "test"
plg := plugin.TestPlugin(t, conn, name)
hc := hostplugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId(), hostplugin.WithSecretsHmac([]byte("foobar")))
@ -208,27 +247,7 @@ func TestGet_Plugin(t *testing.T) {
Id: hc.GetPublicId(),
}
pHostCatalog := &pb.HostCatalog{
Id: hc.GetPublicId(),
ScopeId: hc.GetProjectId(),
Scope: &scopepb.ScopeInfo{
Id: proj.GetPublicId(),
Type: scope.Project.String(),
ParentScopeId: proj.GetParentId(),
},
PluginId: plg.GetPublicId(),
Plugin: &plugins.PluginInfo{
Id: plg.GetPublicId(),
Name: plg.GetName(),
Description: plg.GetDescription(),
},
CreatedTime: hc.CreateTime.GetTimestamp(),
UpdatedTime: hc.UpdateTime.GetTimestamp(),
Type: hostplugin.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: authorizedCollectionActions[hostplugin.Subtype],
SecretsHmac: base58.Encode([]byte("foobar")),
}
pHostCatalog := pluginCatalogToProto(hc, plg, proj)
cases := []struct {
name string
@ -277,7 +296,7 @@ func TestGet_Plugin(t *testing.T) {
req := proto.Clone(toMerge).(*pbs.GetHostCatalogRequest)
proto.Merge(req, tc.req)
s, err := host_catalogs.NewService(ctx, repo, pluginHostRepo, pluginRepo, iamRepoFn)
s, err := host_catalogs.NewService(ctx, repo, pluginHostRepo, pluginRepo, iamRepoFn, catalogServiceFn, 1000)
require.NoError(err, "Couldn't create a new host catalog service.")
got, gErr := s.GetHostCatalog(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req)
@ -324,24 +343,17 @@ func TestList(t *testing.T) {
return static.NewRepository(ctx, rw, rw, kms)
}
iamRepo := iam.TestRepo(t, conn, wrapper)
catalogServiceFn := func() (*host.CatalogRepository, error) {
return host.NewCatalogRepository(ctx, rw, rw)
}
_, pNoCatalogs := iam.TestScopes(t, iamRepo)
oWithCatalogs, pWithCatalogs := iam.TestScopes(t, iamRepo)
oWithOtherCatalogs, pWithOtherCatalogs := iam.TestScopes(t, iamRepo)
_, pWithCatalogs := iam.TestScopes(t, iamRepo)
_, pWithOtherCatalogs := iam.TestScopes(t, iamRepo)
var wantSomeCatalogs []*pb.HostCatalog
for _, hc := range static.TestCatalogs(t, conn, pWithCatalogs.GetPublicId(), 3) {
wantSomeCatalogs = append(wantSomeCatalogs, &pb.HostCatalog{
Id: hc.GetPublicId(),
ScopeId: hc.GetProjectId(),
CreatedTime: hc.GetCreateTime().GetTimestamp(),
UpdatedTime: hc.GetUpdateTime().GetTimestamp(),
Scope: &scopepb.ScopeInfo{Id: pWithCatalogs.GetPublicId(), Type: scope.Project.String(), ParentScopeId: oWithCatalogs.GetPublicId()},
Version: 1,
Type: "static",
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: authorizedCollectionActions[static.Subtype],
})
wantSomeCatalogs = append(wantSomeCatalogs, staticCatalogToProto(hc, pWithCatalogs))
}
var testPluginCatalogs []*pb.HostCatalog
@ -349,63 +361,20 @@ func TestList(t *testing.T) {
plg := plugin.TestPlugin(t, conn, name)
for i := 0; i < 3; i++ {
hc := hostplugin.TestCatalog(t, conn, pWithCatalogs.GetPublicId(), plg.GetPublicId())
cat := &pb.HostCatalog{
Id: hc.GetPublicId(),
ScopeId: hc.GetProjectId(),
CreatedTime: hc.GetCreateTime().GetTimestamp(),
UpdatedTime: hc.GetUpdateTime().GetTimestamp(),
Scope: &scopepb.ScopeInfo{Id: pWithCatalogs.GetPublicId(), Type: scope.Project.String(), ParentScopeId: oWithCatalogs.GetPublicId()},
PluginId: plg.GetPublicId(),
Plugin: &plugins.PluginInfo{
Id: plg.GetPublicId(),
Name: plg.GetName(),
Description: plg.GetDescription(),
},
Version: 1,
Type: hostplugin.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: authorizedCollectionActions[hostplugin.Subtype],
}
cat := pluginCatalogToProto(hc, plg, pWithCatalogs)
wantSomeCatalogs = append(wantSomeCatalogs, cat)
testPluginCatalogs = append(testPluginCatalogs, cat)
}
var wantOtherCatalogs []*pb.HostCatalog
for _, hc := range static.TestCatalogs(t, conn, pWithOtherCatalogs.GetPublicId(), 3) {
wantOtherCatalogs = append(wantOtherCatalogs, &pb.HostCatalog{
Id: hc.GetPublicId(),
ScopeId: hc.GetProjectId(),
CreatedTime: hc.GetCreateTime().GetTimestamp(),
UpdatedTime: hc.GetUpdateTime().GetTimestamp(),
Scope: &scopepb.ScopeInfo{Id: pWithOtherCatalogs.GetPublicId(), Type: scope.Project.String(), ParentScopeId: oWithOtherCatalogs.GetPublicId()},
Version: 1,
Type: "static",
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: authorizedCollectionActions[static.Subtype],
})
wantOtherCatalogs = append(wantOtherCatalogs, staticCatalogToProto(hc, pWithOtherCatalogs))
}
name = "different"
diffPlg := plugin.TestPlugin(t, conn, name)
for i := 0; i < 3; i++ {
hc := hostplugin.TestCatalog(t, conn, pWithOtherCatalogs.GetPublicId(), diffPlg.GetPublicId())
wantOtherCatalogs = append(wantOtherCatalogs, &pb.HostCatalog{
Id: hc.GetPublicId(),
ScopeId: hc.GetProjectId(),
CreatedTime: hc.GetCreateTime().GetTimestamp(),
UpdatedTime: hc.GetUpdateTime().GetTimestamp(),
Scope: &scopepb.ScopeInfo{Id: pWithOtherCatalogs.GetPublicId(), Type: scope.Project.String(), ParentScopeId: oWithOtherCatalogs.GetPublicId()},
PluginId: diffPlg.GetPublicId(),
Plugin: &plugins.PluginInfo{
Id: diffPlg.GetPublicId(),
Name: diffPlg.GetName(),
Description: diffPlg.GetDescription(),
},
Version: 1,
Type: hostplugin.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: authorizedCollectionActions[hostplugin.Subtype],
})
for _, hc := range hostplugin.TestCatalogs(t, conn, pWithOtherCatalogs.GetPublicId(), diffPlg.GetPublicId(), 3) {
wantOtherCatalogs = append(wantOtherCatalogs, pluginCatalogToProto(hc, diffPlg, pWithOtherCatalogs))
}
cases := []struct {
@ -417,17 +386,34 @@ func TestList(t *testing.T) {
{
name: "List Some Catalogs",
req: &pbs.ListHostCatalogsRequest{ScopeId: pWithCatalogs.GetPublicId()},
res: &pbs.ListHostCatalogsResponse{Items: wantSomeCatalogs},
res: &pbs.ListHostCatalogsResponse{
Items: wantSomeCatalogs,
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
EstItemCount: 6,
},
},
{
name: "List Other Catalogs",
req: &pbs.ListHostCatalogsRequest{ScopeId: pWithOtherCatalogs.GetPublicId()},
res: &pbs.ListHostCatalogsResponse{Items: wantOtherCatalogs},
res: &pbs.ListHostCatalogsResponse{
Items: wantOtherCatalogs,
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
EstItemCount: 6,
},
},
{
name: "List No Catalogs",
req: &pbs.ListHostCatalogsRequest{ScopeId: pNoCatalogs.GetPublicId()},
res: &pbs.ListHostCatalogsResponse{},
res: &pbs.ListHostCatalogsResponse{
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
EstItemCount: 0,
},
},
{
name: "Unfound Catalogs",
@ -443,7 +429,11 @@ func TestList(t *testing.T) {
name: "List recursively",
req: &pbs.ListHostCatalogsRequest{ScopeId: scope.Global.String(), Recursive: true},
res: &pbs.ListHostCatalogsResponse{
Items: append(wantSomeCatalogs, wantOtherCatalogs...),
Items: append(wantSomeCatalogs, wantOtherCatalogs...),
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
EstItemCount: 12,
},
},
{
@ -452,7 +442,13 @@ func TestList(t *testing.T) {
ScopeId: scope.Global.String(), Recursive: true,
Filter: fmt.Sprintf(`"/item/scope/id"==%q`, pWithCatalogs.GetPublicId()),
},
res: &pbs.ListHostCatalogsResponse{Items: wantSomeCatalogs},
res: &pbs.ListHostCatalogsResponse{
Items: wantSomeCatalogs,
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
EstItemCount: 6,
},
},
{
name: "Filter To Catalog Using Test Plugin",
@ -460,12 +456,23 @@ func TestList(t *testing.T) {
ScopeId: scope.Global.String(), Recursive: true,
Filter: `"/item/plugin/name"=="test"`,
},
res: &pbs.ListHostCatalogsResponse{Items: testPluginCatalogs},
res: &pbs.ListHostCatalogsResponse{
Items: testPluginCatalogs,
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
EstItemCount: 3,
},
},
{
name: "Filter To No Catalogs",
req: &pbs.ListHostCatalogsRequest{ScopeId: pWithCatalogs.GetPublicId(), Filter: `"/item/id"=="doesnt match"`},
res: &pbs.ListHostCatalogsResponse{},
res: &pbs.ListHostCatalogsResponse{
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
EstItemCount: 0,
},
},
{
name: "Filter Bad Format",
@ -476,7 +483,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 := host_catalogs.NewService(ctx, repoFn, pluginHostRepo, pluginRepo, iamRepoFn)
s, err := host_catalogs.NewService(ctx, repoFn, pluginHostRepo, pluginRepo, iamRepoFn, catalogServiceFn, 1000)
require.NoError(err, "Couldn't create new auth_method service.")
// Test with non-anon user
@ -486,12 +493,6 @@ func TestList(t *testing.T) {
assert.True(errors.Is(gErr, tc.err), "ListHostCatalogs() for scope %q got error %v, wanted %v", tc.req.GetScopeId(), gErr, tc.err)
return
}
sort.Slice(got.Items, func(i, j int) bool {
return got.Items[i].GetId() < got.Items[j].GetId()
})
sort.Slice(tc.res.Items, func(i, j int) bool {
return tc.res.Items[i].GetId() < tc.res.Items[j].GetId()
})
assert.Empty(cmp.Diff(
got,
@ -503,7 +504,8 @@ func TestList(t *testing.T) {
cmpopts.SortSlices(func(a, b protocmp.Message) bool {
return a.String() < b.String()
}),
), "ListHostCatalogs() for scope %q got response %q, wanted %q", tc.req.GetScopeId(), got, tc.res)
protocmp.IgnoreFields(&pbs.ListHostCatalogsResponse{}, "list_token"),
))
// Test with anon user
got, gErr = s.ListHostCatalogs(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetScopeId(), auth.WithUserId(globals.AnonymousUserId)), tc.req)
@ -518,6 +520,374 @@ func TestList(t *testing.T) {
}
}
func TestListPagination(t *testing.T) {
// Set database read timeout to avoid duplicates in response
oldReadTimeout := globals.RefreshReadLookbackDuration
globals.RefreshReadLookbackDuration = 0
t.Cleanup(func() {
globals.RefreshReadLookbackDuration = oldReadTimeout
})
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
sqlDB, err := conn.SqlDB(ctx)
require.NoError(t, err)
rw := db.New(conn)
wrapper := db.TestWrapper(t)
kms := kms.TestKms(t, conn, wrapper)
sche := scheduler.TestScheduler(t, conn, wrapper)
plg1 := plugin.TestPlugin(t, conn, "testplugin1")
plg2 := plugin.TestPlugin(t, conn, "testplugin2")
lp, err := loopback.NewLoopbackPlugin()
require.NoError(t, err)
plgm := map[string]plgpb.HostPluginServiceClient{
plg1.GetPublicId(): loopback.NewWrappingPluginHostClient(lp),
plg2.GetPublicId(): loopback.NewWrappingPluginHostClient(lp),
}
iamRepoFn := func() (*iam.Repository, error) {
return iam.TestRepo(t, conn, wrapper), nil
}
pluginHostRepoFn := func() (*hostplugin.Repository, error) {
return hostplugin.NewRepository(ctx, rw, rw, kms, sche, plgm)
}
pluginRepoFn := func() (*plugin.Repository, error) {
return plugin.NewRepository(ctx, rw, rw, kms)
}
staticRepoFn := func() (*static.Repository, error) {
return static.NewRepository(ctx, rw, rw, kms)
}
tokenRepoFn := func() (*authtoken.Repository, error) {
return authtoken.NewRepository(ctx, rw, rw, kms)
}
serversRepoFn := func() (*server.Repository, error) {
return server.NewRepository(ctx, rw, rw, kms)
}
staticRepo, err := staticRepoFn()
require.NoError(t, err)
pluginRepo, err := pluginHostRepoFn()
require.NoError(t, err)
iamRepo := iam.TestRepo(t, conn, wrapper)
catalogServiceFn := func() (*host.CatalogRepository, error) {
return host.NewCatalogRepository(ctx, rw, rw)
}
org, proj := iam.TestScopes(t, iamRepo)
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
pr := iam.TestRole(t, conn, proj.GetPublicId())
_ = iam.TestUserRole(t, conn, pr.GetPublicId(), at.GetIamUserId())
_ = iam.TestRoleGrant(t, conn, pr.GetPublicId(), "id=*;type=*;actions=*")
gr := iam.TestRole(t, conn, "global")
_ = iam.TestUserRole(t, conn, gr.GetPublicId(), at.GetIamUserId())
_ = iam.TestRoleGrant(t, conn, gr.GetPublicId(), "id=*;type=*;actions=*")
s, err := host_catalogs.NewService(ctx, staticRepoFn, pluginHostRepoFn, pluginRepoFn, iamRepoFn, catalogServiceFn, 1000)
require.NoError(t, err)
var allCatalogs []*pb.HostCatalog
for i := 0; i < 5; i++ {
plg := plg1
if i%2 == 0 {
// Create plugin catalogs with both plugins
plg = plg2
}
allCatalogs = append(allCatalogs, staticCatalogToProto(static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0], proj))
allCatalogs = append(allCatalogs, pluginCatalogToProto(hostplugin.TestCatalogs(t, conn, proj.GetPublicId(), plg.GetPublicId(), 1)[0], plg, proj))
}
// Reverse slice since we're sorting by create time descending
slices.Reverse(allCatalogs)
// Run analyze to update postgres estimates
_, err = sqlDB.ExecContext(ctx, "analyze")
require.NoError(t, err)
// Test without anon user
requestInfo := authpb.RequestInfo{
TokenFormat: uint32(auth.AuthTokenTypeBearer),
PublicId: at.GetPublicId(),
Token: at.GetToken(),
}
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
ctx = auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
// Start paginating, recursively
req := &pbs.ListHostCatalogsRequest{
ScopeId: "global",
Recursive: true,
Filter: "",
ListToken: "",
PageSize: 2,
}
got, err := s.ListHostCatalogs(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 2)
// Compare without comparing the list token
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListHostCatalogsResponse{
Items: allCatalogs[0:2],
ResponseType: "delta",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 10,
},
cmpopts.SortSlices(func(i, j string) bool {
return i < j
}),
cmpopts.SortSlices(func(a, b protocmp.Message) bool {
return a.String() < b.String()
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostCatalogsResponse{}, "list_token"),
),
)
// Request second page
req.ListToken = got.ListToken
got, err = s.ListHostCatalogs(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 2)
// Compare without comparing the list token
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListHostCatalogsResponse{
Items: allCatalogs[2:4],
ResponseType: "delta",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 10,
},
cmpopts.SortSlices(func(i, j string) bool {
return i < j
}),
cmpopts.SortSlices(func(a, b protocmp.Message) bool {
return a.String() < b.String()
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostCatalogsResponse{}, "list_token"),
),
)
// Request rest of results
req.ListToken = got.ListToken
req.PageSize = 10
got, err = s.ListHostCatalogs(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 6)
// Compare without comparing the list token
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListHostCatalogsResponse{
Items: allCatalogs[4:],
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 10,
},
cmpopts.SortSlices(func(i, j string) bool {
return i < j
}),
cmpopts.SortSlices(func(a, b protocmp.Message) bool {
return a.String() < b.String()
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostCatalogsResponse{}, "list_token"),
),
)
// Create another few host catalogs
// Append to start since they are the most recently created.
newStaticCatalog := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
newPluginCatalog := hostplugin.TestCatalog(t, conn, proj.GetPublicId(), plg1.GetPublicId())
allCatalogs = append(
[]*pb.HostCatalog{
pluginCatalogToProto(newPluginCatalog, plg1, proj),
staticCatalogToProto(newStaticCatalog, proj),
},
allCatalogs...,
)
// Delete some of the other catalogs
_, err = staticRepo.DeleteCatalog(ctx, allCatalogs[len(allCatalogs)-1].Id)
require.NoError(t, err)
deletedCatalog1 := allCatalogs[len(allCatalogs)-1]
allCatalogs = allCatalogs[:len(allCatalogs)-1]
_, err = pluginRepo.DeleteCatalog(ctx, allCatalogs[len(allCatalogs)-1].Id)
require.NoError(t, err)
deletedCatalog2 := allCatalogs[len(allCatalogs)-1]
allCatalogs = allCatalogs[:len(allCatalogs)-1]
// Update some other catalogs
allCatalogs[len(allCatalogs)-1].Name = wrapperspb.String("new-name")
allCatalogs[len(allCatalogs)-1].Version = 2
updatedCatalog1 := &static.HostCatalog{
HostCatalog: &sstore.HostCatalog{
PublicId: allCatalogs[len(allCatalogs)-1].GetId(),
Name: allCatalogs[len(allCatalogs)-1].GetName().GetValue(),
ProjectId: allCatalogs[len(allCatalogs)-1].GetScopeId(),
},
}
cat1, _, err := staticRepo.UpdateCatalog(ctx, updatedCatalog1, 1, []string{"name"})
require.NoError(t, err)
allCatalogs[len(allCatalogs)-1].UpdatedTime = cat1.HostCatalog.UpdateTime.GetTimestamp()
allCatalogs[len(allCatalogs)-1].Version = cat1.GetVersion()
// Add to the front since it's most recently updated
allCatalogs = append(
[]*pb.HostCatalog{allCatalogs[len(allCatalogs)-1]},
allCatalogs[:len(allCatalogs)-1]...,
)
allCatalogs[len(allCatalogs)-1].Name = wrapperspb.String("new-name")
allCatalogs[len(allCatalogs)-1].Version = 2
updatedCatalog2 := &hostplugin.HostCatalog{
HostCatalog: &pstore.HostCatalog{
PublicId: allCatalogs[len(allCatalogs)-1].GetId(),
Name: allCatalogs[len(allCatalogs)-1].GetName().GetValue(),
ProjectId: allCatalogs[len(allCatalogs)-1].GetScopeId(),
},
}
cat2, _, _, err := pluginRepo.UpdateCatalog(ctx, updatedCatalog2, 1, []string{"name"})
require.NoError(t, err)
allCatalogs[len(allCatalogs)-1].UpdatedTime = cat2.HostCatalog.UpdateTime.GetTimestamp()
allCatalogs[len(allCatalogs)-1].Version = cat2.GetVersion()
// Add to the front since it's most recently updated
allCatalogs = append(
[]*pb.HostCatalog{allCatalogs[len(allCatalogs)-1]},
allCatalogs[:len(allCatalogs)-1]...,
)
// Run analyze to update postgres estimates
_, err = sqlDB.ExecContext(ctx, "analyze")
require.NoError(t, err)
// Request updated results
req.ListToken = got.ListToken
req.PageSize = 2
got, err = s.ListHostCatalogs(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 2)
// Compare without comparing the list token
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListHostCatalogsResponse{
// The first two should be the recently updated catalogs
Items: allCatalogs[:2],
ResponseType: "delta",
ListToken: "",
SortBy: "updated_time",
SortDir: "desc",
// Should contain the deleted catalogs
RemovedIds: []string{deletedCatalog1.Id, deletedCatalog2.Id},
EstItemCount: 10,
},
cmpopts.SortSlices(func(i, j string) bool {
return i < j
}),
cmpopts.SortSlices(func(a, b protocmp.Message) bool {
return a.String() < b.String()
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostCatalogsResponse{}, "list_token"),
),
)
// Get the next page
req.ListToken = got.ListToken
got, err = s.ListHostCatalogs(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 2)
// Compare without comparing the list token
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListHostCatalogsResponse{
// The next two should be the recently created catalogs
Items: allCatalogs[2:4],
ResponseType: "complete",
ListToken: "",
SortBy: "updated_time",
SortDir: "desc",
// Should be empty again
RemovedIds: nil,
EstItemCount: 10,
},
cmpopts.SortSlices(func(i, j string) bool {
return i < j
}),
cmpopts.SortSlices(func(a, b protocmp.Message) bool {
return a.String() < b.String()
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostCatalogsResponse{}, "list_token"),
),
)
// Request new page with filter requiring looping
// to fill the page.
req.ListToken = ""
req.PageSize = 1
req.Filter = fmt.Sprintf(`"/item/id"==%q or "/item/id"==%q`, allCatalogs[len(allCatalogs)-2].Id, allCatalogs[len(allCatalogs)-1].Id)
got, err = s.ListHostCatalogs(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 1)
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListHostCatalogsResponse{
Items: []*pb.HostCatalog{allCatalogs[len(allCatalogs)-2]},
ResponseType: "delta",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
// Should be empty again
RemovedIds: nil,
EstItemCount: 10,
},
cmpopts.SortSlices(func(i, j string) bool {
return i < j
}),
cmpopts.SortSlices(func(a, b protocmp.Message) bool {
return a.String() < b.String()
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostCatalogsResponse{}, "list_token"),
),
)
req.ListToken = got.ListToken
// Get the second page
got, err = s.ListHostCatalogs(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 1)
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListHostCatalogsResponse{
Items: []*pb.HostCatalog{allCatalogs[len(allCatalogs)-1]},
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 10,
},
cmpopts.SortSlices(func(i, j string) bool {
return i < j
}),
cmpopts.SortSlices(func(a, b protocmp.Message) bool {
return a.String() < b.String()
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostCatalogsResponse{}, "list_token"),
),
)
}
func TestDelete_Static(t *testing.T) {
t.Parallel()
ctx := context.Background()
@ -540,9 +910,12 @@ func TestDelete_Static(t *testing.T) {
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
catalogServiceFn := func() (*host.CatalogRepository, error) {
return host.NewCatalogRepository(ctx, rw, rw)
}
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
s, err := host_catalogs.NewService(ctx, repo, pluginHostRepo, pluginRepo, iamRepoFn)
s, err := host_catalogs.NewService(ctx, repo, pluginHostRepo, pluginRepo, iamRepoFn, catalogServiceFn, 1000)
require.NoError(t, err, "Couldn't create a new host catalog service.")
cases := []struct {
@ -611,10 +984,13 @@ func TestDelete_Plugin(t *testing.T) {
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
catalogServiceFn := func() (*host.CatalogRepository, error) {
return host.NewCatalogRepository(ctx, rw, rw)
}
plg := plugin.TestPlugin(t, conn, "test")
hc := hostplugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
s, err := host_catalogs.NewService(ctx, repo, pluginHostRepo, pluginRepo, iamRepoFn)
s, err := host_catalogs.NewService(ctx, repo, pluginHostRepo, pluginRepo, iamRepoFn, catalogServiceFn, 1000)
require.NoError(t, err, "Couldn't create a new host catalog service.")
cases := []struct {
@ -684,9 +1060,12 @@ func TestDelete_twice(t *testing.T) {
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
catalogServiceFn := func() (*host.CatalogRepository, error) {
return host.NewCatalogRepository(testCtx, rw, rw)
}
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
s, err := host_catalogs.NewService(testCtx, repo, pluginHostRepo, pluginRepo, iamRepoFn)
s, err := host_catalogs.NewService(testCtx, repo, pluginHostRepo, pluginRepo, iamRepoFn, catalogServiceFn, 1000)
require.NoError(err, "Couldn't create a new host catalog service.")
req := &pbs.DeleteHostCatalogRequest{
Id: hc.GetPublicId(),
@ -721,6 +1100,9 @@ func TestCreate_Static(t *testing.T) {
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
catalogServiceFn := func() (*host.CatalogRepository, error) {
return host.NewCatalogRepository(ctx, rw, rw)
}
defaultHc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
defaultHcCreated := defaultHc.GetCreateTime().GetTimestamp().AsTime()
toMerge := &pbs.CreateHostCatalogRequest{}
@ -820,7 +1202,7 @@ func TestCreate_Static(t *testing.T) {
req := proto.Clone(toMerge).(*pbs.CreateHostCatalogRequest)
proto.Merge(req, tc.req)
s, err := host_catalogs.NewService(ctx, repo, pluginHostRepo, pluginRepo, iamRepoFn)
s, err := host_catalogs.NewService(ctx, repo, pluginHostRepo, pluginRepo, iamRepoFn, catalogServiceFn, 1000)
require.NoError(err, "Failed to create a new host catalog service.")
got, gErr := s.CreateHostCatalog(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req)
@ -881,6 +1263,9 @@ func TestCreate_Plugin(t *testing.T) {
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
catalogServiceFn := func() (*host.CatalogRepository, error) {
return host.NewCatalogRepository(ctx, rw, rw)
}
name := "test"
plg := plugin.TestPlugin(t, conn, name)
@ -1006,7 +1391,7 @@ func TestCreate_Plugin(t *testing.T) {
req := proto.Clone(toMerge).(*pbs.CreateHostCatalogRequest)
proto.Merge(req, tc.req)
s, err := host_catalogs.NewService(ctx, repo, pluginHostRepo, pluginRepo, iamRepoFn)
s, err := host_catalogs.NewService(ctx, repo, pluginHostRepo, pluginRepo, iamRepoFn, catalogServiceFn, 1000)
require.NoError(err, "Failed to create a new host catalog service.")
got, gErr := s.CreateHostCatalog(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req)
@ -1072,7 +1457,10 @@ func TestUpdate_Static(t *testing.T) {
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
tested, err := host_catalogs.NewService(ctx, repoFn, pluginHostRepo, pluginRepo, iamRepoFn)
catalogServiceFn := func() (*host.CatalogRepository, error) {
return host.NewCatalogRepository(ctx, rw, rw)
}
tested, err := host_catalogs.NewService(ctx, repoFn, pluginHostRepo, pluginRepo, iamRepoFn, catalogServiceFn, 1000)
require.NoError(t, err, "Failed to create a new host catalog service.")
hc, err := static.NewHostCatalog(ctx, proj.GetPublicId(), static.WithName("default"), static.WithDescription("default"))
@ -1346,7 +1734,7 @@ func TestUpdate_Static(t *testing.T) {
name: "Cant specify Updated Time",
req: &pbs.UpdateHostCatalogRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"updated_time"},
Paths: []string{"created_time"},
},
Item: &pb.HostCatalog{
UpdatedTime: timestamppb.Now(),
@ -1460,7 +1848,10 @@ func TestUpdate_Plugin(t *testing.T) {
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
tested, err := host_catalogs.NewService(testCtx, repoFn, pluginHostRepo, pluginRepo, iamRepoFn)
catalogServiceFn := func() (*host.CatalogRepository, error) {
return host.NewCatalogRepository(testCtx, rw, rw)
}
tested, err := host_catalogs.NewService(testCtx, repoFn, pluginHostRepo, pluginRepo, iamRepoFn, catalogServiceFn, 1000)
require.NoError(t, err, "Failed to create a new host catalog service.")
ctx := auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId())
@ -1710,7 +2101,7 @@ func TestUpdate_Plugin(t *testing.T) {
},
{
name: "Cant specify Updated Time",
masks: []string{"updated_time"},
masks: []string{"created_time"},
changes: []updateFn{
clearReadOnlyFields(),
func(c *pb.HostCatalog) {

@ -1483,6 +1483,21 @@
"in": "query",
"required": false,
"type": "string"
},
{
"name": "list_token",
"description": "An opaque token used to continue an existing iteration or\nrequest updated items. If not specified, pagination\nwill start from the beginning.",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "page_size",
"description": "The maximum size of a page in this iteration.\nIf unset, the default page size configured will be used.\nIf the page_size is greater than the default page configured,\nan error will be returned.",
"in": "query",
"required": false,
"type": "integer",
"format": "int64"
}
],
"tags": [
@ -7933,6 +7948,34 @@
"type": "object",
"$ref": "#/definitions/controller.api.resources.hostcatalogs.v1.HostCatalog"
}
},
"response_type": {
"type": "string",
"description": "The type of response, either \"delta\" or \"complete\".\nDelta signifies that this is part of a paginated result\nor an update to a previously completed pagination.\nComplete signifies that it is the last page."
},
"list_token": {
"type": "string",
"description": "An opaque token used to continue an existing pagination or\nrequest updated items. Use this token in the next list request\nto request the next page."
},
"sort_by": {
"type": "string",
"description": "The name of the field which the items are sorted by."
},
"sort_dir": {
"type": "string",
"description": "The direction of the sort, either \"asc\" or \"desc\"."
},
"removed_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "A list of item IDs that have been removed since they were returned\nas part of a pagination. They should be dropped from any client cache.\nThis may contain items that are not known to the cache, if they were\ncreated and deleted between listings."
},
"est_item_count": {
"type": "integer",
"format": "int64",
"description": "An estimate at the total items available. This may change during pagination."
}
}
},

@ -130,6 +130,15 @@ type ListHostCatalogsRequest struct {
ScopeId string `protobuf:"bytes,1,opt,name=scope_id,proto3" json:"scope_id,omitempty" class:"public" eventstream:"observation"` // @gotags: `class:"public" eventstream:"observation"`
Recursive bool `protobuf:"varint,20,opt,name=recursive,proto3" json:"recursive,omitempty" class:"public" eventstream:"observation"` // @gotags: `class:"public" eventstream:"observation"`
Filter string `protobuf:"bytes,30,opt,name=filter,proto3" json:"filter,omitempty" class:"public"` // @gotags: `class:"public"`
// An opaque token used to continue an existing iteration or
// request updated items. If not specified, pagination
// will start from the beginning.
ListToken string `protobuf:"bytes,40,opt,name=list_token,proto3" json:"list_token,omitempty" class:"public"` // @gotags: `class:"public"`
// The maximum size of a page in this iteration.
// If unset, the default page size configured will be used.
// If the page_size is greater than the default page configured,
// an error will be returned.
PageSize uint32 `protobuf:"varint,50,opt,name=page_size,proto3" json:"page_size,omitempty" class:"public"` // @gotags: `class:"public"`
}
func (x *ListHostCatalogsRequest) Reset() {
@ -185,12 +194,46 @@ func (x *ListHostCatalogsRequest) GetFilter() string {
return ""
}
func (x *ListHostCatalogsRequest) GetListToken() string {
if x != nil {
return x.ListToken
}
return ""
}
func (x *ListHostCatalogsRequest) GetPageSize() uint32 {
if x != nil {
return x.PageSize
}
return 0
}
type ListHostCatalogsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Items []*hostcatalogs.HostCatalog `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"`
// The type of response, either "delta" or "complete".
// Delta signifies that this is part of a paginated result
// or an update to a previously completed pagination.
// Complete signifies that it is the last page.
ResponseType string `protobuf:"bytes,2,opt,name=response_type,proto3" json:"response_type,omitempty" class:"public"` // @gotags: `class:"public"`
// An opaque token used to continue an existing pagination or
// request updated items. Use this token in the next list request
// to request the next page.
ListToken string `protobuf:"bytes,3,opt,name=list_token,proto3" json:"list_token,omitempty" class:"public"` // @gotags: `class:"public"`
// The name of the field which the items are sorted by.
SortBy string `protobuf:"bytes,4,opt,name=sort_by,proto3" json:"sort_by,omitempty" class:"public"` // @gotags: `class:"public"`
// The direction of the sort, either "asc" or "desc".
SortDir string `protobuf:"bytes,5,opt,name=sort_dir,proto3" json:"sort_dir,omitempty" class:"public"` // @gotags: `class:"public"`
// A list of item IDs that have been removed since they were returned
// as part of a pagination. They should be dropped from any client cache.
// This may contain items that are not known to the cache, if they were
// created and deleted between listings.
RemovedIds []string `protobuf:"bytes,6,rep,name=removed_ids,proto3" json:"removed_ids,omitempty" class:"public"` // @gotags: `class:"public"`
// An estimate at the total items available. This may change during pagination.
EstItemCount uint32 `protobuf:"varint,7,opt,name=est_item_count,proto3" json:"est_item_count,omitempty" class:"public"` // @gotags: `class:"public"`
}
func (x *ListHostCatalogsResponse) Reset() {
@ -232,6 +275,48 @@ func (x *ListHostCatalogsResponse) GetItems() []*hostcatalogs.HostCatalog {
return nil
}
func (x *ListHostCatalogsResponse) GetResponseType() string {
if x != nil {
return x.ResponseType
}
return ""
}
func (x *ListHostCatalogsResponse) GetListToken() string {
if x != nil {
return x.ListToken
}
return ""
}
func (x *ListHostCatalogsResponse) GetSortBy() string {
if x != nil {
return x.SortBy
}
return ""
}
func (x *ListHostCatalogsResponse) GetSortDir() string {
if x != nil {
return x.SortDir
}
return ""
}
func (x *ListHostCatalogsResponse) GetRemovedIds() []string {
if x != nil {
return x.RemovedIds
}
return nil
}
func (x *ListHostCatalogsResponse) GetEstItemCount() uint32 {
if x != nil {
return x.EstItemCount
}
return 0
}
type CreateHostCatalogRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -569,127 +654,143 @@ var file_controller_api_services_v1_host_catalog_service_proto_rawDesc = []byte{
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74,
0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74,
0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x6b, 0x0a, 0x17, 0x4c, 0x69,
0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69,
0x64, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18, 0x14,
0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x12,
0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52,
0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x67, 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, 0x48,
0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xa9, 0x01, 0x0a, 0x17, 0x4c,
0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f,
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f,
0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18,
0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65,
0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x6c, 0x69, 0x73, 0x74,
0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6c, 0x69,
0x73, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65,
0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x61, 0x67,
0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xad, 0x02, 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, 0x48,
0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f,
0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f,
0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73,
0x22, 0x87, 0x01, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43,
0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x49, 0x0a,
0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73,
0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c,
0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c,
0x6f, 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x6c, 0x75, 0x67,
0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70,
0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x78, 0x0a, 0x19, 0x43, 0x72,
0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x49, 0x0a, 0x04, 0x69, 0x74, 0x65,
0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e,
0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04,
0x69, 0x74, 0x65, 0x6d, 0x22, 0xb2, 0x01, 0x0a, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48,
0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69,
0x64, 0x12, 0x49, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63,
0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43,
0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x3b, 0x0a, 0x0b,
0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x0a, 0x75,
0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0x66, 0x0a, 0x19, 0x55, 0x70, 0x64,
0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e,
0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e,
0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04, 0x69, 0x74, 0x65,
0x6d, 0x22, 0x2a, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43,
0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a,
0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x1b, 0x0a,
0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c,
0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xde, 0x07, 0x0a, 0x12, 0x48,
0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x12, 0xbd, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74,
0x61, 0x6c, 0x6f, 0x67, 0x12, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76,
0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61,
0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, 0x92, 0x41, 0x1d,
0x12, 0x1b, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20,
0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x82, 0xd3, 0xe4,
0x93, 0x02, 0x1e, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x68,
0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x7b, 0x69, 0x64,
0x7d, 0x12, 0xba, 0x01, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61,
0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61,
0x6c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72,
0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73,
0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x3b, 0x92, 0x41, 0x1f, 0x12, 0x1d, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x6c,
0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61,
0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31,
0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0xc2,
0x01, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74,
0x61, 0x6c, 0x6f, 0x67, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76,
0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61,
0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e,
0x12, 0x24, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x74, 0x79, 0x70,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x74,
0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6c, 0x69, 0x73, 0x74,
0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62,
0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79,
0x12, 0x1a, 0x0a, 0x08, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x05, 0x20, 0x01,
0x28, 0x09, 0x52, 0x08, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x12, 0x20, 0x0a, 0x0b,
0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28,
0x09, 0x52, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x26,
0x0a, 0x0e, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x74, 0x65, 0x6d,
0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x87, 0x01, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x49, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73,
0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73,
0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x20,
0x0a, 0x0b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
0x22, 0x78, 0x0a, 0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61,
0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a,
0x03, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12,
0x49, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74,
0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74,
0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xb2, 0x01, 0x0a, 0x18, 0x55,
0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x49, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x76, 0x31,
0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x04, 0x69, 0x74,
0x65, 0x6d, 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73,
0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d,
0x61, 0x73, 0x6b, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x22,
0x66, 0x0a, 0x19, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74,
0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x04,
0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f,
0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f,
0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x2a, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74,
0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x02, 0x69, 0x64, 0x22, 0x1b, 0x0a, 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73,
0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x32, 0xde, 0x07, 0x0a, 0x12, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67,
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xbd, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x48,
0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x31, 0x2e, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f,
0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43,
0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x6f,
0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x40, 0x92, 0x41, 0x18, 0x12, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x73, 0x20,
0x61, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x82, 0xd3,
0xe4, 0x93, 0x02, 0x1f, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d,
0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c,
0x6f, 0x67, 0x73, 0x12, 0xc7, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x6f,
0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73,
0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,
0x65, 0x22, 0x44, 0x92, 0x41, 0x1d, 0x12, 0x1b, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73,
0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c,
0x6f, 0x67, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12,
0x16, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f,
0x67, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xba, 0x01, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74,
0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x33, 0x2e, 0x63,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f,
0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c,
0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3b, 0x92, 0x41, 0x1f, 0x12, 0x1d, 0x47, 0x65,
0x74, 0x73, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x48, 0x6f, 0x73,
0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02,
0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61,
0x6c, 0x6f, 0x67, 0x73, 0x12, 0xc2, 0x01, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48,
0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f,
0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72,
0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x40, 0x92, 0x41, 0x18, 0x12, 0x16, 0x43, 0x72,
0x65, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74,
0x61, 0x6c, 0x6f, 0x67, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d,
0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74,
0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0xc7, 0x01, 0x0a, 0x11, 0x55, 0x70,
0x64, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12,
0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64,
0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x45, 0x92, 0x41, 0x18, 0x12, 0x16, 0x55, 0x70, 0x64,
0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61,
0x6c, 0x6f, 0x67, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62,
0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d,
0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xbb, 0x01,
0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61,
0x6c, 0x6f, 0x67, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31,
0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c,
0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e,
0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74,
0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x45, 0x92, 0x41,
0x18, 0x12, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x48, 0x6f, 0x73,
0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x3a,
0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0x16, 0x2f, 0x76, 0x31,
0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x7b,
0x69, 0x64, 0x7d, 0x12, 0xbb, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f,
0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73,
0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x39, 0x92, 0x41, 0x18, 0x12, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x73, 0x20, 0x61,
0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x82, 0xd3, 0xe4,
0x93, 0x02, 0x18, 0x2a, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61,
0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x42, 0x55, 0xa2, 0xe3, 0x29,
0x04, 0x68, 0x6f, 0x73, 0x74, 0x5a, 0x4b, 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, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65,
0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69,
0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c,
0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x92, 0x41, 0x18, 0x12, 0x16, 0x44, 0x65, 0x6c,
0x65, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x43, 0x61, 0x74, 0x61,
0x6c, 0x6f, 0x67, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x2a, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x68,
0x6f, 0x73, 0x74, 0x2d, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x7b, 0x69, 0x64,
0x7d, 0x42, 0x55, 0xa2, 0xe3, 0x29, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x5a, 0x4b, 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, 0x69, 0x6e, 0x74, 0x65, 0x72,
0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b,
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

@ -85,10 +85,39 @@ message ListHostCatalogsRequest {
string scope_id = 1 [json_name = "scope_id"]; // @gotags: `class:"public" eventstream:"observation"`
bool recursive = 20 [json_name = "recursive"]; // @gotags: `class:"public" eventstream:"observation"`
string filter = 30 [json_name = "filter"]; // @gotags: `class:"public"`
// An opaque token used to continue an existing iteration or
// request updated items. If not specified, pagination
// will start from the beginning.
string list_token = 40 [json_name = "list_token"]; // @gotags: `class:"public"`
// The maximum size of a page in this iteration.
// If unset, the default page size configured will be used.
// If the page_size is greater than the default page configured,
// an error will be returned.
uint32 page_size = 50 [json_name = "page_size"]; // @gotags: `class:"public"`
}
message ListHostCatalogsResponse {
repeated api.resources.hostcatalogs.v1.HostCatalog items = 1;
// The type of response, either "delta" or "complete".
// Delta signifies that this is part of a paginated result
// or an update to a previously completed pagination.
// Complete signifies that it is the last page.
string response_type = 2 [json_name = "response_type"]; // @gotags: `class:"public"`
// An opaque token used to continue an existing pagination or
// request updated items. Use this token in the next list request
// to request the next page.
string list_token = 3 [json_name = "list_token"]; // @gotags: `class:"public"`
// The name of the field which the items are sorted by.
string sort_by = 4 [json_name = "sort_by"]; // @gotags: `class:"public"`
// The direction of the sort, either "asc" or "desc".
string sort_dir = 5 [json_name = "sort_dir"]; // @gotags: `class:"public"`
// A list of item IDs that have been removed since they were returned
// as part of a pagination. They should be dropped from any client cache.
// This may contain items that are not known to the cache, if they were
// created and deleted between listings.
repeated string removed_ids = 6 [json_name = "removed_ids"]; // @gotags: `class:"public"`
// An estimate at the total items available. This may change during pagination.
uint32 est_item_count = 7 [json_name = "est_item_count"]; // @gotags: `class:"public"`
}
message CreateHostCatalogRequest {

Loading…
Cancel
Save