Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 78 additions & 66 deletions api/gen/proto/go/teleport/machineid/v1/bot_instance_service.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions api/proto/teleport/machineid/v1/bot_instance_service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ message ListBotInstancesRequest {
// The page_token value returned from a previous ListBotInstances request, if
// any.
string page_token = 3;
// A search term used to filter the results. If non-empty, it's used to match against supported fields.
string filter_search_term = 4;
}

// Response for ListBotInstances.
Expand Down
2 changes: 1 addition & 1 deletion lib/auth/machineid/machineidv1/bot_instance_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func (b *BotInstanceService) ListBotInstances(ctx context.Context, req *pb.ListB
return nil, trace.Wrap(err)
}

res, nextToken, err := b.backend.ListBotInstances(ctx, req.FilterBotName, int(req.PageSize), req.PageToken)
res, nextToken, err := b.backend.ListBotInstances(ctx, req.FilterBotName, int(req.PageSize), req.PageToken, req.FilterSearchTerm)
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down
2 changes: 1 addition & 1 deletion lib/services/bot_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type BotInstance interface {
GetBotInstance(ctx context.Context, botName, instanceID string) (*machineidv1.BotInstance, error)

// ListBotInstances
ListBotInstances(ctx context.Context, botName string, pageSize int, lastToken string) ([]*machineidv1.BotInstance, string, error)
ListBotInstances(ctx context.Context, botName string, pageSize int, lastToken string, search string) ([]*machineidv1.BotInstance, string, error)

// DeleteBotInstance
DeleteBotInstance(ctx context.Context, botName, instanceID string) error
Expand Down
44 changes: 37 additions & 7 deletions lib/services/local/bot_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package local

import (
"context"
"slices"
"strings"

"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
Expand Down Expand Up @@ -89,17 +91,45 @@ func (b *BotInstanceService) GetBotInstance(ctx context.Context, botName, instan
return instance, trace.Wrap(err)
}

// ListBotInstances lists all bot instances matching the given bot name filter.
// If an empty bot name is provided, all bot instances will be fetched.
func (b *BotInstanceService) ListBotInstances(ctx context.Context, botName string, pageSize int, lastKey string) ([]*machineidv1.BotInstance, string, error) {
// If botName is empty, return instances for all bots by not using a service prefix
// ListBotInstances lists all matching bot instances. A bot name and/or search terms can be optionally provided.
// If an non-empty bot name is provided, only instances for that bot will be fetched.
// If an non-empty search term is provided, only instances with a value containing the term in supported fields are fetched.
// Supported search fields include; bot name, instance id, hostname (latest), tbot version (latest), join method (latest)
func (b *BotInstanceService) ListBotInstances(ctx context.Context, botName string, pageSize int, lastKey string, search string) ([]*machineidv1.BotInstance, string, error) {
var service *generic.ServiceWrapper[*machineidv1.BotInstance]
if botName == "" {
r, nextToken, err := b.service.ListResources(ctx, pageSize, lastKey)
// If botName is empty, return instances for all bots by not using a service prefix
service = b.service
} else {
service = b.service.WithPrefix(botName)
}

if search == "" {
r, nextToken, err := service.ListResources(ctx, pageSize, lastKey)
return r, nextToken, trace.Wrap(err)
}

serviceWithPrefix := b.service.WithPrefix(botName)
r, nextToken, err := serviceWithPrefix.ListResources(ctx, pageSize, lastKey)
r, nextToken, err := service.ListResourcesWithFilter(ctx, pageSize, lastKey, func(item *machineidv1.BotInstance) bool {
latestHeartbeats := item.GetStatus().GetLatestHeartbeats()
heartbeat := item.Status.InitialHeartbeat // Use initial heartbeat as a fallback
if len(latestHeartbeats) > 0 {
heartbeat = latestHeartbeats[len(latestHeartbeats)-1]
}

values := []string{
item.Spec.BotName,
item.Spec.InstanceId,
}

if heartbeat != nil {
values = append(values, heartbeat.Hostname, heartbeat.JoinMethod, heartbeat.Version, "v"+heartbeat.Version)
}

return slices.ContainsFunc(values, func(val string) bool {
return strings.Contains(strings.ToLower(val), strings.ToLower(search))
})
})

return r, nextToken, trace.Wrap(err)
}

Expand Down
Loading
Loading