handlers/hosts: implement pagination for hosts

Adds protobuf types and handler logic to support pagination of hosts.
This commit is contained in:
Johan Brandhorst-Satzkorn committed 2024-01-05 07:43:21 -08:00
1 parent cea80413c9
commit 17b6ad5c5c
6 files changed
+1083 -279

No files matched your search

+1 -1
View File
@@ -150,7 +150,7 @@ func (c *Controller) registerGrpcServices(s *grpc.Server) error {
services.RegisterHostSetServiceServer(s, hss)
}
if _, ok := currentServices[services.HostService_ServiceDesc.ServiceName]; !ok {
hs, err := hosts.NewService(c.baseContext, c.StaticHostRepoFn, c.PluginHostRepoFn)
hs, err := hosts.NewService(c.baseContext, c.StaticHostRepoFn, c.PluginHostRepoFn, c.conf.RawConfig.Controller.MaxPageSize)
if err != nil {
return fmt.Errorf("failed to create host handler service: %w", err)
}
@@ -19,12 +19,15 @@ import (
hostplugin "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/listtoken"
"github.com/hashicorp/boundary/internal/pagination"
"github.com/hashicorp/boundary/internal/perms"
plugin "github.com/hashicorp/boundary/internal/plugin"
"github.com/hashicorp/boundary/internal/requests"
"github.com/hashicorp/boundary/internal/types/action"
"github.com/hashicorp/boundary/internal/types/resource"
"github.com/hashicorp/boundary/internal/types/subtypes"
"github.com/hashicorp/boundary/internal/util"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hosts"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins"
"golang.org/x/exp/maps"
@@ -58,8 +61,6 @@ var (
)
)
const domain = "host"
func init() {
var err error
if maskManager, err = handlers.NewMaskManager(
@@ -79,13 +80,14 @@ type Service struct {
staticRepoFn common.StaticRepoFactory
pluginRepoFn common.PluginHostRepoFactory
maxPageSize uint
}
var _ pbs.HostServiceServer = (*Service)(nil)
// NewService returns a host Service which handles host related requests to boundary and uses the provided
// repositories for storage and retrieval.
func NewService(ctx context.Context, repoFn common.StaticRepoFactory, pluginRepoFn common.PluginHostRepoFactory) (Service, error) {
func NewService(ctx context.Context, repoFn common.StaticRepoFactory, pluginRepoFn common.PluginHostRepoFactory, maxPageSize uint) (Service, error) {
const op = "hosts.NewService"
if repoFn == nil {
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing static repository")
@@ -93,73 +95,188 @@ func NewService(ctx context.Context, repoFn common.StaticRepoFactory, pluginRepo
if pluginRepoFn == nil {
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing plugin host repository")
}
return Service{staticRepoFn: repoFn, pluginRepoFn: pluginRepoFn}, nil
if maxPageSize == 0 {
maxPageSize = uint(globals.DefaultMaxPageSize)
}
return Service{staticRepoFn: repoFn, pluginRepoFn: pluginRepoFn, maxPageSize: maxPageSize}, nil
}
func (s Service) ListHosts(ctx context.Context, req *pbs.ListHostsRequest) (*pbs.ListHostsResponse, error) {
const op = "hosts.(Service).ListHosts"
if err := validateListRequest(ctx, req); err != nil {
return nil, err
return nil, errors.Wrap(ctx, err, op)
}
_, authResults := s.parentAndAuthResult(ctx, req.GetHostCatalogId(), action.List)
if authResults.Error != nil {
return nil, authResults.Error
}
hl, plg, err := s.listFromRepo(ctx, req.GetHostCatalogId())
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())
}
var filterItemFn func(ctx context.Context, item host.Host, plg *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.Host, plg *plugin.Plugin) (bool, error) {
outputOpts, ok := newOutputOpts(ctx, item, plg, authResults)
if !ok {
return false, nil
}
pbItem, err := toProto(ctx, item, outputOpts...)
if err != nil {
return false, err
}
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.Host, plg *plugin.Plugin) (bool, error) {
return true, nil
}
}
grantsHash, err := authResults.GrantsHash(ctx)
if err != nil {
return nil, err
}
if len(hl) == 0 {
return &pbs.ListHostsResponse{}, nil
var listResp *pagination.ListResponse[host.Host]
var sortBy string
var plg *plugin.Plugin
switch globals.ResourceInfoFromPrefix(req.GetHostCatalogId()).Subtype {
case static.Subtype:
// Wrap the filter item func that takes a plugin, since the static host
// domain does not use a plugin.
staticFilterItemFn := func(ctx context.Context, item host.Host) (bool, error) {
return filterItemFn(ctx, item, nil)
}
repo, err := s.staticRepoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if req.GetListToken() == "" {
sortBy = "created_time"
listResp, err = static.ListHosts(ctx, grantsHash, pageSize, staticFilterItemFn, repo, req.GetHostCatalogId())
if err != nil {
return nil, err
}
} else {
listToken, err := handlers.ParseListToken(ctx, req.GetListToken(), resource.Host, grantsHash)
if err != nil {
return nil, err
}
switch st := listToken.Subtype.(type) {
case *listtoken.PaginationToken:
sortBy = "created_time"
listResp, err = static.ListHostsPage(ctx, grantsHash, pageSize, staticFilterItemFn, listToken, repo, req.GetHostCatalogId())
if err != nil {
return nil, err
}
case *listtoken.StartRefreshToken:
sortBy = "updated_time"
listResp, err = static.ListHostsRefresh(ctx, grantsHash, pageSize, staticFilterItemFn, listToken, repo, req.GetHostCatalogId())
if err != nil {
return nil, err
}
case *listtoken.RefreshToken:
sortBy = "updated_time"
listResp, err = static.ListHostsRefreshPage(ctx, grantsHash, pageSize, staticFilterItemFn, listToken, repo, req.GetHostCatalogId())
if err != nil {
return nil, err
}
default:
return nil, handlers.ApiErrorWithCodeAndMessage(codes.InvalidArgument, "unexpected list token subtype: %T", st)
}
}
case hostplugin.Subtype:
repo, err := s.pluginRepoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if req.GetListToken() == "" {
sortBy = "created_time"
listResp, plg, err = hostplugin.ListHosts(ctx, grantsHash, pageSize, filterItemFn, repo, req.GetHostCatalogId())
if err != nil {
return nil, err
}
} else {
listToken, err := handlers.ParseListToken(ctx, req.GetListToken(), resource.Host, grantsHash)
if err != nil {
return nil, err
}
switch st := listToken.Subtype.(type) {
case *listtoken.PaginationToken:
sortBy = "created_time"
listResp, plg, err = hostplugin.ListHostsPage(ctx, grantsHash, pageSize, filterItemFn, listToken, repo, req.GetHostCatalogId())
if err != nil {
return nil, err
}
case *listtoken.StartRefreshToken:
sortBy = "updated_time"
listResp, plg, err = hostplugin.ListHostsRefresh(ctx, grantsHash, pageSize, filterItemFn, listToken, repo, req.GetHostCatalogId())
if err != nil {
return nil, err
}
case *listtoken.RefreshToken:
sortBy = "updated_time"
listResp, plg, err = hostplugin.ListHostsRefreshPage(ctx, grantsHash, pageSize, filterItemFn, listToken, repo, req.GetHostCatalogId())
if err != nil {
return nil, err
}
default:
return nil, handlers.ApiErrorWithCodeAndMessage(codes.InvalidArgument, "unexpected list token subtype: %T", st)
}
}
}
filter, err := handlers.NewFilter(ctx, req.GetFilter())
if err != nil {
return nil, err
}
finalItems := make([]*pb.Host, 0, len(hl))
res := perms.Resource{
ScopeId: authResults.Scope.Id,
Type: resource.Host,
Pin: req.GetHostCatalogId(),
}
for _, item := range hl {
res.Id = item.GetPublicId()
idActions := idActionsTypeMap[globals.ResourceInfoFromPrefix(res.Id).Subtype]
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), idActions, auth.WithResource(&res)).Strings()
if len(authorizedActions) == 0 {
finalItems := make([]*pb.Host, 0, len(listResp.Items))
for _, item := range listResp.Items {
outputOpts, ok := newOutputOpts(ctx, item, plg, authResults)
if !ok {
continue
}
pbItem, err := toProto(ctx, item, outputOpts...)
if err != nil {
continue
}
finalItems = append(finalItems, pbItem)
}
respType := "delta"
if listResp.CompleteListing {
respType = "complete"
}
resp := &pbs.ListHostsResponse{
Items: finalItems,
EstItemCount: uint32(listResp.EstimatedItemCount),
RemovedIds: listResp.DeletedIds,
ResponseType: respType,
SortBy: sortBy,
SortDir: "desc",
}
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))
}
if outputFields.Has(globals.AuthorizedActionsField) {
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
}
outputOpts = append(outputOpts, handlers.WithHostSetIds(item.GetSetIds()))
item, err := toProto(ctx, item, outputOpts...)
if listResp.ListToken != nil {
resp.ListToken, err = handlers.MarshalListToken(ctx, listResp.ListToken, pbs.ResourceType_RESOURCE_TYPE_HOST)
if err != nil {
return nil, 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, item)
if err != nil {
return nil, err
}
if filter.Match(filterable) {
finalItems = append(finalItems, item)
}
}
return &pbs.ListHostsResponse{Items: finalItems}, nil
return resp, nil
}
// GetHost implements the interface pbs.HostServiceServer.
@@ -315,7 +432,7 @@ func (s Service) getFromRepo(ctx context.Context, id string) (host.Host, *plugin
if err != nil {
return nil, nil, err
}
if h == nil {
if util.IsNil(h) {
return nil, nil, handlers.NotFoundErrorf("Host %q doesn't exist.", id)
}
case hostplugin.Subtype:
@@ -417,39 +534,6 @@ func (s Service) deleteFromRepo(ctx context.Context, projectId, id string) (bool
return rows > 0, nil
}
func (s Service) listFromRepo(ctx context.Context, catalogId string) ([]host.Host, *plugins.PluginInfo, error) {
var hosts []host.Host
var plg *plugins.PluginInfo
switch globals.ResourceInfoFromPrefix(catalogId).Subtype {
case static.Subtype:
repo, err := s.staticRepoFn()
if err != nil {
return nil, nil, err
}
hl, err := repo.ListHosts(ctx, catalogId, static.WithLimit(-1))
if err != nil {
return nil, nil, err
}
for _, h := range hl {
hosts = append(hosts, h)
}
case hostplugin.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)
}
return hosts, plg, nil
}
func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type) (host.Catalog, auth.VerifyResults) {
res := auth.VerifyResults{}
staticRepo, err := s.staticRepoFn()
@@ -536,6 +620,36 @@ func toPluginInfo(plg *plugin.Plugin) *plugins.PluginInfo {
}
}
func newOutputOpts(ctx context.Context, item host.Host, plg *plugin.Plugin, authResults auth.VerifyResults) ([]handlers.Option, bool) {
res := perms.Resource{
ScopeId: authResults.Scope.Id,
Type: resource.Host,
Pin: item.GetCatalogId(),
Id: item.GetPublicId(),
}
res.Id = item.GetPublicId()
idActions := idActionsTypeMap[globals.ResourceInfoFromPrefix(res.Id).Subtype]
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), idActions, auth.WithResource(&res)).Strings()
if len(authorizedActions) == 0 {
return nil, false
}
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(toPluginInfo(plg)))
}
if outputFields.Has(globals.ScopeField) {
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
}
if outputFields.Has(globals.AuthorizedActionsField) {
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
}
outputOpts = append(outputOpts, handlers.WithHostSetIds(item.GetSetIds()))
return outputOpts, true
}
func toProto(ctx context.Context, in host.Host, opt ...handlers.Option) (*pb.Host, error) {
opts := handlers.GetOpts(opt...)
if opts.WithOutputFields == nil {
@@ -7,7 +7,7 @@ import (
"context"
"errors"
"fmt"
"sort"
"slices"
"strings"
"testing"
@@ -15,18 +15,24 @@ 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/hosts"
"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"
"github.com/hashicorp/boundary/internal/host/static"
"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/hosts"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins"
@@ -47,6 +53,46 @@ var testAuthorizedActions = map[globals.Subtype][]string{
hostplugin.Subtype: {"no-op", "read"},
}
func staticHostToProto(h host.Host, proj *iam.Scope, hostSet *static.HostSet) *pb.Host {
return &pb.Host{
Id: h.GetPublicId(),
HostCatalogId: h.GetCatalogId(),
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: proj.GetParentId()},
CreatedTime: h.GetCreateTime().GetTimestamp(),
UpdatedTime: h.GetUpdateTime().GetTimestamp(),
Version: h.GetVersion(),
Type: static.Subtype.String(),
Attrs: &pb.Host_StaticHostAttributes{
StaticHostAttributes: &pb.StaticHostAttributes{
Address: wrapperspb.String(h.GetAddress()),
},
},
AuthorizedActions: testAuthorizedActions[static.Subtype],
HostSetIds: []string{hostSet.GetPublicId()},
}
}
func pluginHostToProto(h host.Host, proj *iam.Scope, hostSet *hostplugin.HostSet, plg *plugin.Plugin, extId string, extName string) *pb.Host {
return &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: proj.GetParentId()},
CreatedTime: h.GetCreateTime().GetTimestamp(),
UpdatedTime: h.GetUpdateTime().GetTimestamp(),
HostSetIds: []string{hostSet.GetPublicId()},
Version: 1,
ExternalId: extId,
ExternalName: extName,
Type: hostplugin.Subtype.String(),
AuthorizedActions: testAuthorizedActions[hostplugin.Subtype],
}
}
func TestGet_Static(t *testing.T) {
t.Parallel()
ctx := context.Background()
@@ -59,7 +105,7 @@ func TestGet_Static(t *testing.T) {
return iamRepo, nil
}
org, proj := iam.TestScopes(t, iamRepo)
_, proj := iam.TestScopes(t, iamRepo)
rw := db.New(conn)
sche := scheduler.TestScheduler(t, conn, wrapper)
@@ -74,21 +120,7 @@ func TestGet_Static(t *testing.T) {
s := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
static.TestSetMembers(t, conn, s.GetPublicId(), []*static.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: "static",
Attrs: &pb.Host_StaticHostAttributes{
StaticHostAttributes: &pb.StaticHostAttributes{
Address: wrapperspb.String(h.GetAddress()),
},
},
AuthorizedActions: testAuthorizedActions[static.Subtype],
HostSetIds: []string{s.GetPublicId()},
}
pHost := staticHostToProto(h, proj, s)
cases := []struct {
name string
@@ -123,7 +155,7 @@ func TestGet_Static(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(ctx, repoFn, pluginRepoFn)
s, err := hosts.NewService(ctx, repoFn, pluginRepoFn, 1000)
require.NoError(err, "Couldn't create a new host service.")
got, gErr := s.GetHost(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
@@ -159,7 +191,7 @@ func TestGet_Plugin(t *testing.T) {
return iamRepo, nil
}
org, proj := iam.TestScopes(t, iamRepo)
_, proj := iam.TestScopes(t, iamRepo)
plg := plugin.TestPlugin(t, conn, "test")
plgm := map[string]plgpb.HostPluginServiceClient{
@@ -183,23 +215,7 @@ func TestGet_Plugin(t *testing.T) {
hs := hostplugin.TestSet(t, conn, kms, sche, hc, plgm)
hostplugin.TestSetMembers(t, conn, hs.GetPublicId(), []*hostplugin.Host{h, hPrev})
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: hostplugin.Subtype.String(),
Plugin: &plugins.PluginInfo{
Id: plg.GetPublicId(),
Name: plg.GetName(),
Description: plg.GetDescription(),
},
HostSetIds: []string{hs.GetPublicId()},
ExternalId: "test",
ExternalName: "test-ext-name",
AuthorizedActions: testAuthorizedActions[hostplugin.Subtype],
}
pHost := pluginHostToProto(h, proj, hs, plg, "test", "test-ext-name")
cases := []struct {
name string
@@ -247,7 +263,7 @@ func TestGet_Plugin(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(ctx, repoFn, pluginRepoFn)
s, err := hosts.NewService(ctx, repoFn, pluginRepoFn, 1000)
require.NoError(err, "Couldn't create a new host service.")
got, gErr := s.GetHost(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
@@ -284,7 +300,7 @@ func TestList_Static(t *testing.T) {
return iamRepo, nil
}
org, proj := iam.TestScopes(t, iamRepo)
_, proj := iam.TestScopes(t, iamRepo)
rw := db.New(conn)
sche := scheduler.TestScheduler(t, conn, wrapper)
@@ -302,26 +318,10 @@ func TestList_Static(t *testing.T) {
testHosts := static.TestHosts(t, conn, hc.GetPublicId(), 10)
static.TestSetMembers(t, conn, hset.GetPublicId(), testHosts)
for _, h := range testHosts {
wantHs = append(wantHs, &pb.Host{
Id: h.GetPublicId(),
HostCatalogId: h.GetCatalogId(),
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()},
CreatedTime: h.GetCreateTime().GetTimestamp(),
UpdatedTime: h.GetUpdateTime().GetTimestamp(),
Version: h.GetVersion(),
Type: static.Subtype.String(),
Attrs: &pb.Host_StaticHostAttributes{
StaticHostAttributes: &pb.StaticHostAttributes{
Address: wrapperspb.String(h.GetAddress()),
},
},
AuthorizedActions: testAuthorizedActions[static.Subtype],
HostSetIds: []string{hset.GetPublicId()},
})
wantHs = append(wantHs, staticHostToProto(h, proj, hset))
}
sort.Slice(wantHs, func(i int, j int) bool {
return wantHs[i].GetId() < wantHs[j].GetId()
})
slices.Reverse(wantHs)
cases := []struct {
name string
@@ -332,7 +332,13 @@ func TestList_Static(t *testing.T) {
{
name: "List Many Hosts",
req: &pbs.ListHostsRequest{HostCatalogId: hc.GetPublicId()},
res: &pbs.ListHostsResponse{Items: wantHs},
res: &pbs.ListHostsResponse{
Items: wantHs,
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
EstItemCount: 10,
},
},
{
name: "List Non Existing Hosts",
@@ -342,17 +348,31 @@ func TestList_Static(t *testing.T) {
{
name: "List No Hosts",
req: &pbs.ListHostsRequest{HostCatalogId: hcNoHosts.GetPublicId()},
res: &pbs.ListHostsResponse{},
res: &pbs.ListHostsResponse{
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
},
},
{
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]},
res: &pbs.ListHostsResponse{
Items: wantHs[1:2],
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
EstItemCount: 1,
},
},
{
name: "Filter to No Hosts",
req: &pbs.ListHostsRequest{HostCatalogId: hc.GetPublicId(), Filter: `"/item/id"=="doesnt match"`},
res: &pbs.ListHostsResponse{},
res: &pbs.ListHostsResponse{
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
},
},
{
name: "Filter Bad Format",
@@ -363,7 +383,7 @@ func TestList_Static(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(ctx, repoFn, pluginRepoFn)
s, err := hosts.NewService(ctx, repoFn, pluginRepoFn, 1000)
require.NoError(err, "Couldn't create new host set service.")
// Test non-anonymous listing
@@ -377,11 +397,12 @@ func TestList_Static(t *testing.T) {
assert.Empty(cmp.Diff(
got,
tc.res,
protocmp.Transform(),
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
), "ListHosts(%q) got response %q, wanted %q", tc.req, got, tc.res)
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostsResponse{}, "list_token"),
))
// Test anonymous listing
got, gErr = s.ListHosts(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId(), auth.WithUserId(globals.AnonymousUserId)), tc.req)
@@ -407,7 +428,7 @@ func TestList_Plugin(t *testing.T) {
return iamRepo, nil
}
org, proj := iam.TestScopes(t, iamRepo)
_, proj := iam.TestScopes(t, iamRepo)
plg := plugin.TestPlugin(t, conn, "test")
plgm := map[string]plgpb.HostPluginServiceClient{
plg.GetPublicId(): loopback.NewWrappingPluginHostClient(&loopback.TestPluginServer{}),
@@ -427,31 +448,14 @@ func TestList_Plugin(t *testing.T) {
var wantHs []*pb.Host
for i := 0; i < 10; i++ {
extId := fmt.Sprintf("host %d", i)
h := hostplugin.TestHost(t, conn, hc.GetPublicId(), extId, hostplugin.WithExternalName(fmt.Sprintf("ext-name-%d", i)))
extId := fmt.Sprintf("ext-id-%d", i)
extName := fmt.Sprintf("ext-name-%d", i)
h := hostplugin.TestHost(t, conn, hc.GetPublicId(), extId, hostplugin.WithExternalName(extName))
hostplugin.TestSetMembers(t, conn, hs.GetPublicId(), []*hostplugin.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,
ExternalId: extId,
ExternalName: fmt.Sprintf("ext-name-%d", i),
Type: hostplugin.Subtype.String(),
AuthorizedActions: testAuthorizedActions[hostplugin.Subtype],
})
wantHs = append(wantHs, pluginHostToProto(h, proj, hs, plg, extId, extName))
}
sort.Slice(wantHs, func(i, j int) bool {
return wantHs[i].GetId() < wantHs[j].GetId()
})
slices.Reverse(wantHs)
cases := []struct {
name string
@@ -462,7 +466,13 @@ func TestList_Plugin(t *testing.T) {
{
name: "List Many Hosts",
req: &pbs.ListHostsRequest{HostCatalogId: hc.GetPublicId()},
res: &pbs.ListHostsResponse{Items: wantHs},
res: &pbs.ListHostsResponse{
Items: wantHs,
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
EstItemCount: 10,
},
},
{
name: "List Non Existing Hosts",
@@ -472,17 +482,31 @@ func TestList_Plugin(t *testing.T) {
{
name: "List No Hosts",
req: &pbs.ListHostsRequest{HostCatalogId: hcNoHosts.GetPublicId()},
res: &pbs.ListHostsResponse{},
res: &pbs.ListHostsResponse{
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
},
},
{
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]},
res: &pbs.ListHostsResponse{
Items: wantHs[1:2],
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
EstItemCount: 1,
},
},
{
name: "Filter to No Hosts",
req: &pbs.ListHostsRequest{HostCatalogId: hc.GetPublicId(), Filter: `"/item/id"=="doesnt match"`},
res: &pbs.ListHostsResponse{},
res: &pbs.ListHostsResponse{
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
},
},
{
name: "Filter Bad Format",
@@ -493,7 +517,7 @@ func TestList_Plugin(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(ctx, repoFn, pluginRepoFn)
s, err := hosts.NewService(ctx, repoFn, pluginRepoFn, 1000)
require.NoError(err, "Couldn't create new host set service.")
// Test non-anonymous listing
@@ -505,9 +529,6 @@ func TestList_Plugin(t *testing.T) {
}
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,
@@ -515,7 +536,8 @@ func TestList_Plugin(t *testing.T) {
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
), "ListHosts(%q) got response %q, wanted %q", tc.req, got, tc.res)
protocmp.IgnoreFields(&pbs.ListHostsResponse{}, "list_token"),
))
// Test anonymous listing
got, gErr = s.ListHosts(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId(), auth.WithUserId(globals.AnonymousUserId)), tc.req)
@@ -530,6 +552,501 @@ func TestList_Plugin(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")
wrapper := db.TestWrapper(t)
sqlDB, err := conn.SqlDB(ctx)
require.NoError(t, err)
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 := plugin.TestPlugin(t, conn, "test")
plgm := map[string]plgpb.HostPluginServiceClient{
plg.GetPublicId(): loopback.NewWrappingPluginHostClient(&loopback.TestPluginServer{}),
}
rw := db.New(conn)
sche := scheduler.TestScheduler(t, conn, wrapper)
tokenRepoFn := func() (*authtoken.Repository, error) {
return authtoken.NewRepository(ctx, rw, rw, kms)
}
serversRepoFn := func() (*server.Repository, error) {
return server.NewRepository(ctx, rw, rw, kms)
}
pluginRepoFn := func() (*hostplugin.Repository, error) {
return hostplugin.NewRepository(ctx, rw, rw, kms, sche, plgm)
}
staticRepoFn := func() (*static.Repository, error) {
return static.NewRepository(ctx, rw, rw, kms)
}
staticRepo, err := staticRepoFn()
require.NoError(t, err)
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
r := iam.TestRole(t, conn, proj.GetPublicId())
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
phc := hostplugin.TestCatalogs(t, conn, proj.GetPublicId(), plg.GetPublicId(), 1)[0]
phs := hostplugin.TestSet(t, conn, kms, sche, phc, plgm)
shc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
shs := static.TestSets(t, conn, shc.GetPublicId(), 1)[0]
var staticPbHosts []*pb.Host
staticHosts := static.TestHosts(t, conn, shc.GetPublicId(), 10)
static.TestSetMembers(t, conn, shs.GetPublicId(), staticHosts)
for _, host := range staticHosts {
staticPbHosts = append(staticPbHosts, staticHostToProto(host, proj, shs))
}
var pluginPbHosts []*pb.Host
for i := 0; i < 10; i++ {
extId := fmt.Sprintf("ext-id-%d", i)
extName := fmt.Sprintf("ext-name-%d", i)
pluginHost := hostplugin.TestHost(t, conn, phc.GetPublicId(), extId, hostplugin.WithExternalName(extName))
hostplugin.TestSetMembers(t, conn, phs.GetPublicId(), []*hostplugin.Host{pluginHost})
pluginPbHosts = append(pluginPbHosts, pluginHostToProto(pluginHost, proj, phs, plg, extId, extName))
}
// Since we list by create_time descending, we need to reverse slices
slices.Reverse(staticPbHosts)
slices.Reverse(pluginPbHosts)
// 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)
s, err := hosts.NewService(ctx, staticRepoFn, pluginRepoFn, 1000)
require.NoError(t, err)
t.Run("static-hosts", func(t *testing.T) {
// Start paginating, recursively
req := &pbs.ListHostsRequest{
HostCatalogId: shc.GetPublicId(),
Filter: "",
ListToken: "",
PageSize: 2,
}
got, err := s.ListHosts(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.ListHostsResponse{
Items: staticPbHosts[0:2],
ResponseType: "delta",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 10,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostsResponse{}, "list_token"),
),
)
// Request second page
req.ListToken = got.ListToken
got, err = s.ListHosts(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.ListHostsResponse{
Items: staticPbHosts[2:4],
ResponseType: "delta",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 10,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostsResponse{}, "list_token"),
),
)
// Request rest of results
req.ListToken = got.ListToken
req.PageSize = 10
got, err = s.ListHosts(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.ListHostsResponse{
Items: staticPbHosts[4:],
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 10,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostsResponse{}, "list_token"),
),
)
// Create another Host
staticHost := static.TestHosts(t, conn, shc.GetPublicId(), 1)[0]
static.TestSetMembers(t, conn, shs.GetPublicId(), []*static.Host{staticHost})
// Add to the front since it's most recently updated
staticPbHosts = append([]*pb.Host{staticHostToProto(staticHost, proj, shs)}, staticPbHosts...)
// Delete one of the other Hosts
_, err = staticRepo.DeleteHost(ctx, proj.GetPublicId(), staticPbHosts[len(staticPbHosts)-1].Id)
require.NoError(t, err)
deletedHost := staticPbHosts[len(staticPbHosts)-1]
staticPbHosts = staticPbHosts[:len(staticPbHosts)-1]
// Update one of the other Hosts
staticPbHosts[1].Name = wrapperspb.String("new-name")
staticPbHosts[1].Version = 2
h := &static.Host{
Host: &store.Host{
PublicId: staticPbHosts[1].Id,
CatalogId: staticPbHosts[1].HostCatalogId,
Name: staticPbHosts[1].Name.GetValue(),
},
}
newHost, _, err := staticRepo.UpdateHost(ctx, proj.PublicId, h, 1, []string{"name"})
require.NoError(t, err)
staticPbHosts[1].UpdatedTime = newHost.GetUpdateTime().GetTimestamp()
staticPbHosts[1].Version = newHost.GetVersion()
// Add to the front since it's most recently updated
staticPbHosts = append(
[]*pb.Host{staticPbHosts[1]},
append(
[]*pb.Host{staticPbHosts[0]},
staticPbHosts[2:]...,
)...,
)
// Run analyze to update postgres estimates
_, err = sqlDB.ExecContext(ctx, "analyze")
require.NoError(t, err)
// Request updated results
req.ListToken = got.ListToken
req.PageSize = 1
got, err = s.ListHosts(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 1)
// Compare without comparing the list token
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListHostsResponse{
Items: []*pb.Host{staticPbHosts[0]},
ResponseType: "delta",
ListToken: "",
SortBy: "updated_time",
SortDir: "desc",
// Should contain the deleted Host
RemovedIds: []string{deletedHost.Id},
EstItemCount: 10,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostsResponse{}, "list_token"),
),
)
// Get next page
req.ListToken = got.ListToken
got, err = s.ListHosts(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 1)
// Compare without comparing the list token
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListHostsResponse{
Items: []*pb.Host{staticPbHosts[1]},
ResponseType: "complete",
ListToken: "",
SortBy: "updated_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 10,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostsResponse{}, "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`, staticPbHosts[len(staticPbHosts)-2].Id, staticPbHosts[len(staticPbHosts)-1].Id)
got, err = s.ListHosts(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 1)
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListHostsResponse{
Items: []*pb.Host{staticPbHosts[len(staticPbHosts)-2]},
ResponseType: "delta",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
// Should be empty again
RemovedIds: nil,
EstItemCount: 10,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostsResponse{}, "list_token"),
),
)
// Get the second page
req.ListToken = got.ListToken
got, err = s.ListHosts(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 1)
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListHostsResponse{
Items: []*pb.Host{staticPbHosts[len(staticPbHosts)-1]},
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 10,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostsResponse{}, "list_token"),
),
)
})
t.Run("plugin-hosts", func(t *testing.T) {
// Start paginating, recursively
req := &pbs.ListHostsRequest{
HostCatalogId: phc.GetPublicId(),
Filter: "",
ListToken: "",
PageSize: 2,
}
got, err := s.ListHosts(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.ListHostsResponse{
Items: pluginPbHosts[0:2],
ResponseType: "delta",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 10,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostsResponse{}, "list_token"),
),
)
// Request second page
req.ListToken = got.ListToken
got, err = s.ListHosts(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.ListHostsResponse{
Items: pluginPbHosts[2:4],
ResponseType: "delta",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 10,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostsResponse{}, "list_token"),
),
)
// Request rest of results
req.ListToken = got.ListToken
req.PageSize = 10
got, err = s.ListHosts(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.ListHostsResponse{
Items: pluginPbHosts[4:],
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 10,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostsResponse{}, "list_token"),
),
)
// Create another Host
extId := "ext-id-10"
extName := "ext-name-10"
pluginHost := hostplugin.TestHost(t, conn, phc.GetPublicId(), extId, hostplugin.WithExternalName(extName))
hostplugin.TestSetMembers(t, conn, phs.GetPublicId(), []*hostplugin.Host{pluginHost})
// Add to the front since it's most recently updated
pluginPbHosts = append([]*pb.Host{pluginHostToProto(pluginHost, proj, phs, plg, extId, extName)}, pluginPbHosts...)
// Note: it's non-trivial to delete and update plugin hosts, so we skip that part here.
// Run analyze to update postgres estimates
_, err = sqlDB.ExecContext(ctx, "analyze")
require.NoError(t, err)
// Request updated results
req.ListToken = got.ListToken
got, err = s.ListHosts(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 1)
// Compare without comparing the list token
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListHostsResponse{
Items: []*pb.Host{pluginPbHosts[0]},
ResponseType: "complete",
ListToken: "",
SortBy: "updated_time",
SortDir: "desc",
EstItemCount: 11,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostsResponse{}, "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`, pluginPbHosts[len(pluginPbHosts)-2].Id, pluginPbHosts[len(pluginPbHosts)-1].Id)
got, err = s.ListHosts(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 1)
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListHostsResponse{
Items: []*pb.Host{pluginPbHosts[len(pluginPbHosts)-2]},
ResponseType: "delta",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 11,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostsResponse{}, "list_token"),
),
)
req.ListToken = got.ListToken
// Get the second page
got, err = s.ListHosts(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 1)
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListHostsResponse{
Items: []*pb.Host{pluginPbHosts[len(pluginPbHosts)-1]},
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 11,
},
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListHostsResponse{}, "list_token"),
),
)
})
}
func TestDelete(t *testing.T) {
t.Parallel()
ctx := context.Background()
@@ -559,7 +1076,7 @@ func TestDelete(t *testing.T) {
pluginHc := hostplugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
pluginH := hostplugin.TestHost(t, conn, pluginHc.GetPublicId(), "test")
s, err := hosts.NewService(ctx, repoFn, pluginRepoFn)
s, err := hosts.NewService(ctx, repoFn, pluginRepoFn, 1000)
require.NoError(t, err, "Couldn't create a new host set service.")
cases := []struct {
@@ -647,7 +1164,7 @@ func TestDelete_twice(t *testing.T) {
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0]
s, err := hosts.NewService(testCtx, repoFn, pluginRepoFn)
s, err := hosts.NewService(testCtx, repoFn, pluginRepoFn, 1000)
require.NoError(err, "Couldn't create a new host set service.")
req := &pbs.DeleteHostRequest{
Id: h.GetPublicId(),
@@ -859,7 +1376,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(ctx, repoFn, pluginRepoFn)
s, err := hosts.NewService(ctx, repoFn, pluginRepoFn, 1000)
require.NoError(err, "Failed to create a new host set service.")
got, gErr := s.CreateHost(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
@@ -949,7 +1466,7 @@ func TestUpdate_Static(t *testing.T) {
hCreated := h.GetCreateTime().GetTimestamp().AsTime()
tested, err := hosts.NewService(ctx, repoFn, pluginRepoFn)
tested, err := hosts.NewService(ctx, repoFn, pluginRepoFn, 1000)
require.NoError(t, err, "Failed to create a new host set service.")
cases := []struct {
@@ -1406,7 +1923,7 @@ func TestUpdate_Plugin(t *testing.T) {
hc := hostplugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
h := hostplugin.TestHost(t, conn, hc.GetPublicId(), "test")
tested, err := hosts.NewService(ctx, repoFn, pluginRepoFn)
tested, err := hosts.NewService(ctx, repoFn, pluginRepoFn, 1000)
require.NoError(t, err)
got, err := tested.UpdateHost(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), &pbs.UpdateHostRequest{
+43
View File
@@ -1884,6 +1884,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": [
@@ -7795,6 +7810,34 @@
"type": "object",
"$ref": "#/definitions/controller.api.resources.hosts.v1.Host"
}
},
"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."
}
}
},
@@ -129,6 +129,15 @@ type ListHostsRequest struct {
HostCatalogId string `protobuf:"bytes,1,opt,name=host_catalog_id,proto3" json:"host_catalog_id,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 *ListHostsRequest) Reset() {
@@ -177,12 +186,46 @@ func (x *ListHostsRequest) GetFilter() string {
return ""
}
func (x *ListHostsRequest) GetListToken() string {
if x != nil {
return x.ListToken
}
return ""
}
func (x *ListHostsRequest) GetPageSize() uint32 {
if x != nil {
return x.PageSize
}
return 0
}
type ListHostsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Items []*hosts.Host `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 *ListHostsResponse) Reset() {
@@ -224,6 +267,48 @@ func (x *ListHostsResponse) GetItems() []*hosts.Host {
return nil
}
func (x *ListHostsResponse) GetResponseType() string {
if x != nil {
return x.ResponseType
}
return ""
}
func (x *ListHostsResponse) GetListToken() string {
if x != nil {
return x.ListToken
}
return ""
}
func (x *ListHostsResponse) GetSortBy() string {
if x != nil {
return x.SortBy
}
return ""
}
func (x *ListHostsResponse) GetSortDir() string {
if x != nil {
return x.SortDir
}
return ""
}
func (x *ListHostsResponse) GetRemovedIds() []string {
if x != nil {
return x.RemovedIds
}
return nil
}
func (x *ListHostsResponse) GetEstItemCount() uint32 {
if x != nil {
return x.EstItemCount
}
return 0
}
type CreateHostRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -548,106 +633,122 @@ var file_controller_api_services_v1_host_service_proto_rawDesc = []byte{
0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 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, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73,
0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x54, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x48,
0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x68,
0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c,
0x6f, 0x67, 0x5f, 0x69, 0x64, 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, 0x52, 0x0a,
0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x92, 0x01, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74,
0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x0f,
0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61,
0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 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, 0x98, 0x02, 0x0a,
0x11, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x3d, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x27, 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, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d,
0x73, 0x22, 0x50, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 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, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x04, 0x69,
0x74, 0x65, 0x6d, 0x22, 0x63, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73,
0x74, 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, 0x3b, 0x0a, 0x04, 0x69,
0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 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, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f,
0x73, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x9e, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64,
0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e,
0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x3b,
0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 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, 0x73, 0x2e, 0x76, 0x31,
0x2e, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x3c, 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, 0x0b, 0x75, 0x70,
0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x22, 0x51, 0x0a, 0x12, 0x55, 0x70, 0x64,
0x73, 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, 0x50, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x04,
0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 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, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48,
0x6f, 0x73, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x63, 0x0a, 0x12, 0x43, 0x72, 0x65,
0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 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, 0x73, 0x2e, 0x76,
0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x23, 0x0a, 0x11,
0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69,
0x64, 0x22, 0x14, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xb9, 0x06, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74,
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x98, 0x01, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x48,
0x6f, 0x73, 0x74, 0x12, 0x2a, 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, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x2b, 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, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x92, 0x41,
0x15, 0x12, 0x13, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65,
0x20, 0x48, 0x6f, 0x73, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x62, 0x04, 0x69, 0x74,
0x65, 0x6d, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x2f, 0x7b, 0x69,
0x64, 0x7d, 0x12, 0xa9, 0x01, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73,
0x12, 0x2c, 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, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d,
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, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3f, 0x92,
0x41, 0x2b, 0x12, 0x29, 0x4c, 0x69, 0x73, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x48, 0x6f, 0x73,
0x74, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69,
0x66, 0x69, 0x65, 0x64, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x82, 0xd3, 0xe4,
0x93, 0x02, 0x0b, 0x12, 0x09, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x12, 0xa4,
0x01, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x2d, 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, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 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, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, 0x92, 0x41,
0x17, 0x12, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67,
0x6c, 0x65, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x3a, 0x04,
0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x09, 0x2f, 0x76, 0x31, 0x2f,
0x68, 0x6f, 0x73, 0x74, 0x73, 0x12, 0xa2, 0x01, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
0x48, 0x6f, 0x73, 0x74, 0x12, 0x2d, 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, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x2e, 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, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x35, 0x92, 0x41, 0x10, 0x12, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
0x20, 0x61, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x3a, 0x04,
0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0x0e, 0x2f, 0x76, 0x31, 0x2f,
0x68, 0x6f, 0x73, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x96, 0x01, 0x0a, 0x0a, 0x44,
0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72,
0x69, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x27, 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, 0x73,
0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x9e,
0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x02, 0x69, 0x64, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x27, 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, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65,
0x6d, 0x12, 0x3c, 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, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x22,
0x51, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x27, 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, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x04, 0x69, 0x74,
0x65, 0x6d, 0x22, 0x23, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x14, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74,
0x65, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xb9, 0x06,
0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x98, 0x01,
0x0a, 0x07, 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x2a, 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, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 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,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x92, 0x41, 0x10, 0x12, 0x0e, 0x44,
0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x2e, 0x82, 0xd3, 0xe4,
0x93, 0x02, 0x10, 0x2a, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 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,
0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 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, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x34, 0x92, 0x41, 0x15, 0x12, 0x13, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20,
0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93,
0x02, 0x16, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f,
0x73, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xa9, 0x01, 0x0a, 0x09, 0x4c, 0x69, 0x73,
0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x2c, 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, 0x73, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 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, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x3f, 0x92, 0x41, 0x2b, 0x12, 0x29, 0x4c, 0x69, 0x73, 0x74, 0x20, 0x61,
0x6c, 0x6c, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65,
0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x43, 0x61, 0x74, 0x61, 0x6c,
0x6f, 0x67, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0b, 0x12, 0x09, 0x2f, 0x76, 0x31, 0x2f, 0x68,
0x6f, 0x73, 0x74, 0x73, 0x12, 0xa4, 0x01, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48,
0x6f, 0x73, 0x74, 0x12, 0x2d, 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, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x2e, 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, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x37, 0x92, 0x41, 0x17, 0x12, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20,
0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x2e, 0x82, 0xd3,
0xe4, 0x93, 0x02, 0x17, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d,
0x22, 0x09, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x12, 0xa2, 0x01, 0x0a, 0x0a,
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x2d, 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, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 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, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x92, 0x41, 0x10, 0x12, 0x0e,
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x2e, 0x82, 0xd3,
0xe4, 0x93, 0x02, 0x1c, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d,
0x32, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d,
0x12, 0x96, 0x01, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x12,
0x2d, 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, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e,
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, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29,
0x92, 0x41, 0x10, 0x12, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x20, 0x48, 0x6f,
0x73, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x2a, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x68,
0x6f, 0x73, 0x74, 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 (
@@ -86,10 +86,39 @@ message GetHostResponse {
message ListHostsRequest {
string host_catalog_id = 1 [json_name = "host_catalog_id"]; // @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 ListHostsResponse {
repeated api.resources.hosts.v1.Host 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 CreateHostRequest {