Files
Lucas Manuel RodriguezandCopilot Autofix powered by AI 441e31c705 Move targets and secret variables to server/fleet/ (#46196)
Resolves #36087 (one of several PRs).

## Testing

- [x] QA'd all new/changed functionality manually.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Dry-run support when creating secret variables.

* **Improvements**
* Standardized API models for secret-variables and targets for more
consistent behavior.
  * List secret variables now includes pagination metadata.
* More consistent error reporting across secret-variables and targets
APIs.
* Target search/count behavior refined: pre-selected built-in labels are
omitted as expected.

* **Tests**
* Integration tests updated to validate the new request/response
behavior and target-selection logic.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/fleetdm/fleet/pull/46196?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-03 15:07:54 -03:00

160 lines
4.1 KiB
Go

package service
import (
"context"
"github.com/fleetdm/fleet/v4/server/contexts/viewer"
"github.com/fleetdm/fleet/v4/server/fleet"
)
////////////////////////////////////////////////////////////////////////////////
// Search Targets
////////////////////////////////////////////////////////////////////////////////
func searchTargetsEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (fleet.Errorer, error) {
req := request.(*fleet.SearchTargetsRequest)
results, err := svc.SearchTargets(ctx, req.MatchQuery, req.QueryID, req.Selected)
if err != nil {
return fleet.SearchTargetsResponse{Err: err}, nil
}
targets := &fleet.TargetsData{
Hosts: []*fleet.HostResponse{},
Labels: []fleet.LabelSearchResult{},
Teams: []fleet.TeamSearchResult{},
}
for _, host := range results.Hosts {
targets.Hosts = append(targets.Hosts, fleet.HostResponseForHostCheap(host))
}
for _, label := range results.Labels {
targets.Labels = append(targets.Labels,
fleet.LabelSearchResult{
Label: label,
DisplayText: label.Name,
Count: label.HostCount,
},
)
}
for _, team := range results.Teams {
targets.Teams = append(targets.Teams,
fleet.TeamSearchResult{
Team: team,
DisplayText: team.Name,
Count: team.HostCount,
},
)
}
metrics, err := svc.CountHostsInTargets(ctx, req.QueryID, req.Selected)
if err != nil {
return fleet.SearchTargetsResponse{Err: err}, nil
}
return fleet.SearchTargetsResponse{
Targets: targets,
TargetsCount: metrics.TotalHosts,
TargetsOnline: metrics.OnlineHosts,
TargetsOffline: metrics.OfflineHosts,
TargetsMissingInAction: metrics.MissingInActionHosts,
}, nil
}
func (svc *Service) SearchTargets(ctx context.Context, matchQuery string, queryID *uint, targets fleet.HostTargets) (*fleet.TargetSearchResults, error) {
if err := svc.authz.Authorize(ctx, &fleet.Target{}, fleet.ActionRead); err != nil {
return nil, err
}
vc, ok := viewer.FromContext(ctx)
if !ok {
return nil, fleet.ErrNoContext
}
filter := fleet.TeamFilter{User: vc.User}
if queryID != nil {
query, err := svc.ds.Query(ctx, *queryID)
if err != nil {
return nil, err
}
filter.IncludeObserver = query.ObserverCanRun
filter.ObserverTeamID = query.TeamID
}
results := &fleet.TargetSearchResults{}
hosts, err := svc.ds.SearchHosts(ctx, filter, matchQuery, targets.HostIDs...)
if err != nil {
return nil, err
}
results.Hosts = append(results.Hosts, hosts...)
labels, err := svc.ds.SearchLabels(ctx, filter, matchQuery, targets.LabelIDs...)
if err != nil {
return nil, err
}
results.Labels = labels
teams, err := svc.ds.SearchTeams(ctx, filter, matchQuery, targets.TeamIDs...)
if err != nil {
return nil, err
}
// Strip unnecessary info from target search results.
// The target picker only needs team ID, name, and host count.
for _, t := range teams {
t.Secrets = nil
t.Config.AgentOptions = nil
}
results.Teams = teams
return results, nil
}
func (svc *Service) CountHostsInTargets(ctx context.Context, queryID *uint, targets fleet.HostTargets) (*fleet.TargetMetrics, error) {
if err := svc.authz.Authorize(ctx, &fleet.Target{}, fleet.ActionRead); err != nil {
return nil, err
}
vc, ok := viewer.FromContext(ctx)
if !ok {
return nil, fleet.ErrNoContext
}
filter := fleet.TeamFilter{User: vc.User}
if queryID != nil {
query, err := svc.ds.Query(ctx, *queryID)
if err != nil {
return nil, err
}
filter.IncludeObserver = query.ObserverCanRun
filter.ObserverTeamID = query.TeamID
}
metrics, err := svc.ds.CountHostsInTargets(ctx, filter, targets, svc.clock.Now())
if err != nil {
return nil, err
}
return &metrics, nil
}
func countTargetsEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (fleet.Errorer, error) {
req := request.(*fleet.CountTargetsRequest)
counts, err := svc.CountHostsInTargets(ctx, req.QueryID, req.Selected)
if err != nil {
return fleet.CountTargetsResponse{Err: err}, nil
}
return fleet.CountTargetsResponse{
TargetsCount: counts.TotalHosts,
TargetsOnline: counts.OnlineHosts,
TargetsOffline: counts.OfflineHosts,
}, nil
}