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>
This commit is contained in:
Lucas Manuel Rodriguez
2026-06-03 15:07:54 -03:00
committed by GitHub
co-authored by Copilot Autofix powered by AI
parent 06cfd32e89
commit 441e31c705
12 changed files with 321 additions and 287 deletions
+66
View File
@@ -0,0 +1,66 @@
package fleet
//////////////////////////////////////////////////////////////////////////////////
// Create secret variables (spec)
//////////////////////////////////////////////////////////////////////////////////
type CreateSecretVariablesRequest struct {
DryRun bool `json:"dry_run"`
SecretVariables []SecretVariable `json:"secrets"`
}
type CreateSecretVariablesResponse struct {
Err error `json:"error,omitempty"`
}
func (r CreateSecretVariablesResponse) Error() error { return r.Err }
//////////////////////////////////////////////////////////////////////////////////
// Create secret variable
//////////////////////////////////////////////////////////////////////////////////
type CreateSecretVariableRequest struct {
Name string `json:"name"`
Value string `json:"value"`
}
type CreateSecretVariableResponse struct {
ID uint `json:"id"`
Name string `json:"name"`
Err error `json:"error,omitempty"`
}
func (r CreateSecretVariableResponse) Error() error { return r.Err }
//////////////////////////////////////////////////////////////////////////////////
// List secret variables
//////////////////////////////////////////////////////////////////////////////////
type ListSecretVariablesRequest struct {
ListOptions ListOptions `url:"list_options"`
}
type ListSecretVariablesResponse struct {
CustomVariables []SecretVariableIdentifier `json:"custom_variables"`
Meta *PaginationMetadata `json:"meta"`
Count int `json:"count"`
Err error `json:"error,omitempty"`
}
func (r ListSecretVariablesResponse) Error() error { return r.Err }
//////////////////////////////////////////////////////////////////////////////////
// Delete secret variable
//////////////////////////////////////////////////////////////////////////////////
type DeleteSecretVariableRequest struct {
ID uint `url:"id"`
}
type DeleteSecretVariableResponse struct {
Err error `json:"error,omitempty"`
}
func (r DeleteSecretVariableResponse) Error() error { return r.Err }
+146
View File
@@ -0,0 +1,146 @@
package fleet
import (
"encoding/json"
"errors"
"time"
)
////////////////////////////////////////////////////////////////////////////////
// Search Targets
////////////////////////////////////////////////////////////////////////////////
type SearchTargetsRequest struct {
// MatchQuery is the free-text search query used to match hosts, labels, and teams.
MatchQuery string `json:"query"`
// QueryID is the ID of a saved query to run (used to determine if this is a
// query that observers can run).
QueryID *uint `json:"query_id" renameto:"report_id"`
// Selected is the list of IDs that are already selected on the caller side
// (e.g. the UI), so those are IDs that will be omitted from the returned
// payload.
Selected HostTargets `json:"selected"`
}
type LabelSearchResult struct {
*Label
DisplayText string `json:"display_text"`
Count int `json:"count"`
}
type TeamSearchResult struct {
*Team
DisplayText string `json:"display_text"`
Count int `json:"count"`
}
func (t TeamSearchResult) MarshalJSON() ([]byte, error) {
if t.Team == nil {
return nil, errors.New("team search result: nil team")
}
x := struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
Description string `json:"description"`
TeamConfig
UserCount int `json:"user_count"`
Users []TeamUser `json:"users,omitempty"`
HostCount int `json:"host_count"`
Hosts []HostResponse `json:"hosts,omitempty"`
Secrets []*EnrollSecret `json:"secrets,omitempty"`
DisplayText string `json:"display_text"`
Count int `json:"count"`
}{
ID: t.ID,
CreatedAt: t.CreatedAt,
Name: t.Name,
Description: t.Description,
TeamConfig: t.Config,
UserCount: t.UserCount,
Users: t.Users,
HostCount: t.HostCount,
Hosts: HostResponsesForHostsCheap(t.Hosts),
Secrets: t.Secrets,
DisplayText: t.DisplayText,
Count: t.Count,
}
return json.Marshal(x)
}
func (t *TeamSearchResult) UnmarshalJSON(b []byte) error {
var x struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
Description string `json:"description"`
TeamConfig
UserCount int `json:"user_count"`
Users []TeamUser `json:"users,omitempty"`
HostCount int `json:"host_count"`
Hosts []Host `json:"hosts,omitempty"`
Secrets []*EnrollSecret `json:"secrets,omitempty"`
DisplayText string `json:"display_text"`
Count int `json:"count"`
}
if err := json.Unmarshal(b, &x); err != nil {
return err
}
*t = TeamSearchResult{
Team: &Team{
ID: x.ID,
CreatedAt: x.CreatedAt,
Name: x.Name,
Description: x.Description,
Config: x.TeamConfig,
UserCount: x.UserCount,
Users: x.Users,
HostCount: x.HostCount,
Hosts: x.Hosts,
Secrets: x.Secrets,
},
DisplayText: x.DisplayText,
Count: x.Count,
}
return nil
}
type TargetsData struct {
Hosts []*HostResponse `json:"hosts"`
Labels []LabelSearchResult `json:"labels"`
Teams []TeamSearchResult `json:"teams" renameto:"fleets"`
}
type SearchTargetsResponse struct {
Targets *TargetsData `json:"targets,omitempty"`
TargetsCount uint `json:"targets_count"`
TargetsOnline uint `json:"targets_online"`
TargetsOffline uint `json:"targets_offline"`
TargetsMissingInAction uint `json:"targets_missing_in_action"`
Err error `json:"error,omitempty"`
}
func (r SearchTargetsResponse) Error() error { return r.Err }
////////////////////////////////////////////////////////////////////////////////
// Count Targets
////////////////////////////////////////////////////////////////////////////////
type CountTargetsRequest struct {
Selected HostTargets `json:"selected"`
QueryID *uint `json:"query_id" renameto:"report_id"`
}
type CountTargetsResponse struct {
TargetsCount uint `json:"targets_count"`
TargetsOnline uint `json:"targets_online"`
TargetsOffline uint `json:"targets_offline"`
Err error `json:"error,omitempty"`
}
func (r CountTargetsResponse) Error() error { return r.Err }
+2 -2
View File
@@ -4,10 +4,10 @@ import "github.com/fleetdm/fleet/v4/server/fleet"
func (c *Client) SaveSecretVariables(secretVariables []fleet.SecretVariable, dryRun bool) error {
verb, path := "PUT", "/api/latest/fleet/spec/secret_variables"
params := createSecretVariablesRequest{
params := fleet.CreateSecretVariablesRequest{
SecretVariables: secretVariables,
DryRun: dryRun,
}
var responseBody createSecretVariablesResponse
var responseBody fleet.CreateSecretVariablesResponse
return c.authenticatedRequest(params, verb, path, &responseBody)
}
+2 -2
View File
@@ -6,7 +6,7 @@ import (
// SearchTargets searches for the supplied targets in the Fleet instance.
func (c *Client) SearchTargets(query string, hostIDs, labelIDs []uint) (*fleet.TargetSearchResults, error) {
req := searchTargetsRequest{
req := fleet.SearchTargetsRequest{
MatchQuery: query,
Selected: fleet.HostTargets{
LabelIDs: labelIDs,
@@ -15,7 +15,7 @@ func (c *Client) SearchTargets(query string, hostIDs, labelIDs []uint) (*fleet.T
},
}
verb, path := "POST", "/api/latest/fleet/targets"
var responseBody searchTargetsResponse
var responseBody fleet.SearchTargetsResponse
err := c.authenticatedRequest(req, verb, path, &responseBody)
if err != nil {
return nil, err
+6 -6
View File
@@ -334,8 +334,8 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
ue.GET("/api/_version_/fleet/email/change/{token}", changeEmailEndpoint, changeEmailRequest{})
// TODO: searchTargetsEndpoint will be removed in Fleet 5.0
ue.POST("/api/_version_/fleet/targets", searchTargetsEndpoint, searchTargetsRequest{})
ue.POST("/api/_version_/fleet/targets/count", countTargetsEndpoint, countTargetsRequest{})
ue.POST("/api/_version_/fleet/targets", searchTargetsEndpoint, fleet.SearchTargetsRequest{})
ue.POST("/api/_version_/fleet/targets/count", countTargetsEndpoint, fleet.CountTargetsRequest{})
ue.POST("/api/_version_/fleet/invites", createInviteEndpoint, createInviteRequest{})
ue.GET("/api/_version_/fleet/invites", listInvitesEndpoint, listInvitesRequest{})
@@ -583,10 +583,10 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
ue.POST("/api/_version_/fleet/autofill/policy", autofillPoliciesEndpoint, fleet.AutofillPoliciesRequest{})
// Secret variables
ue.PUT("/api/_version_/fleet/spec/secret_variables", createSecretVariablesEndpoint, createSecretVariablesRequest{})
ue.POST("/api/_version_/fleet/custom_variables", createSecretVariableEndpoint, createSecretVariableRequest{})
ue.GET("/api/_version_/fleet/custom_variables", listSecretVariablesEndpoint, listSecretVariablesRequest{})
ue.DELETE("/api/_version_/fleet/custom_variables/{id:[0-9]+}", deleteSecretVariableEndpoint, deleteSecretVariableRequest{})
ue.PUT("/api/_version_/fleet/spec/secret_variables", createSecretVariablesEndpoint, fleet.CreateSecretVariablesRequest{})
ue.POST("/api/_version_/fleet/custom_variables", createSecretVariableEndpoint, fleet.CreateSecretVariableRequest{})
ue.GET("/api/_version_/fleet/custom_variables", listSecretVariablesEndpoint, fleet.ListSecretVariablesRequest{})
ue.DELETE("/api/_version_/fleet/custom_variables/{id:[0-9]+}", deleteSecretVariableEndpoint, fleet.DeleteSecretVariableRequest{})
// API end-points
ue.GET("/api/_version_/fleet/rest_api", listAPIEndpointsEndpoint, listAPIEndpointsRequest{})
+47 -47
View File
@@ -9540,8 +9540,8 @@ func (s *integrationTestSuite) TestSearchTargets() {
require.Len(t, lblMap, len(builtinNames))
// no search criteria
var searchResp searchTargetsResponse
s.DoJSON("POST", "/api/latest/fleet/targets", searchTargetsRequest{}, http.StatusOK, &searchResp)
var searchResp fleet.SearchTargetsResponse
s.DoJSON("POST", "/api/latest/fleet/targets", fleet.SearchTargetsRequest{}, http.StatusOK, &searchResp)
require.Equal(t, uint(0), searchResp.TargetsCount)
require.Len(t, searchResp.Targets.Hosts, len(hosts)) // the HostTargets.HostIDs are actually host IDs to *omit* from the search
require.Len(t, searchResp.Targets.Labels, len(lblMap))
@@ -9552,22 +9552,22 @@ func (s *integrationTestSuite) TestSearchTargets() {
lblIDs = append(lblIDs, labelID)
}
searchResp = searchTargetsResponse{}
s.DoJSON("POST", "/api/latest/fleet/targets", searchTargetsRequest{Selected: fleet.HostTargets{LabelIDs: lblIDs}}, http.StatusOK, &searchResp)
searchResp = fleet.SearchTargetsResponse{}
s.DoJSON("POST", "/api/latest/fleet/targets", fleet.SearchTargetsRequest{Selected: fleet.HostTargets{LabelIDs: lblIDs}}, http.StatusOK, &searchResp)
require.Equal(t, uint(0), searchResp.TargetsCount)
require.Len(t, searchResp.Targets.Hosts, len(hosts)) // no omitted host id
require.Len(t, searchResp.Targets.Labels, 0) // All built-in labels have been omitted (pre-selected)
require.Len(t, searchResp.Targets.Teams, 0)
searchResp = searchTargetsResponse{}
s.DoJSON("POST", "/api/latest/fleet/targets", searchTargetsRequest{Selected: fleet.HostTargets{HostIDs: []uint{hosts[1].ID}}}, http.StatusOK, &searchResp)
searchResp = fleet.SearchTargetsResponse{}
s.DoJSON("POST", "/api/latest/fleet/targets", fleet.SearchTargetsRequest{Selected: fleet.HostTargets{HostIDs: []uint{hosts[1].ID}}}, http.StatusOK, &searchResp)
require.Equal(t, uint(1), searchResp.TargetsCount)
require.Len(t, searchResp.Targets.Hosts, len(hosts)-1) // one omitted host id
require.Len(t, searchResp.Targets.Labels, len(lblMap)) // labels have not been omitted
require.Len(t, searchResp.Targets.Teams, 0)
searchResp = searchTargetsResponse{}
s.DoJSON("POST", "/api/latest/fleet/targets", searchTargetsRequest{MatchQuery: "foo.local1"}, http.StatusOK, &searchResp)
searchResp = fleet.SearchTargetsResponse{}
s.DoJSON("POST", "/api/latest/fleet/targets", fleet.SearchTargetsRequest{MatchQuery: "foo.local1"}, http.StatusOK, &searchResp)
require.Equal(t, uint(0), searchResp.TargetsCount)
require.Len(t, searchResp.Targets.Hosts, 1)
require.Len(t, searchResp.Targets.Labels, 1) // with a match query, only matching label names and "All Hosts" can be returned (here, only all hosts)
@@ -9673,12 +9673,12 @@ func (s *integrationTestSuite) TestCountTargets() {
err = s.ds.AddHostsToTeam(context.Background(), fleet.NewAddHostsToTeamParams(ptr.Uint(team.ID), []uint{hostIDs[0]}))
require.NoError(t, err)
var countResp countTargetsResponse
var countResp fleet.CountTargetsResponse
// sleep to reduce flake in last seen time so that online/offline counts can be tested
time.Sleep(1 * time.Second)
// none selected
s.DoJSON("POST", "/api/latest/fleet/targets/count", countTargetsRequest{}, http.StatusOK, &countResp)
s.DoJSON("POST", "/api/latest/fleet/targets/count", fleet.CountTargetsRequest{}, http.StatusOK, &countResp)
require.Equal(t, uint(0), countResp.TargetsCount)
require.Equal(t, uint(0), countResp.TargetsOnline)
require.Equal(t, uint(0), countResp.TargetsOffline)
@@ -9688,23 +9688,23 @@ func (s *integrationTestSuite) TestCountTargets() {
lblIDs = append(lblIDs, labelID)
}
// all hosts label selected
countResp = countTargetsResponse{}
s.DoJSON("POST", "/api/latest/fleet/targets/count", countTargetsRequest{Selected: fleet.HostTargets{LabelIDs: lblIDs}}, http.StatusOK, &countResp)
countResp = fleet.CountTargetsResponse{}
s.DoJSON("POST", "/api/latest/fleet/targets/count", fleet.CountTargetsRequest{Selected: fleet.HostTargets{LabelIDs: lblIDs}}, http.StatusOK, &countResp)
require.Equal(t, uint(3), countResp.TargetsCount)
require.Equal(t, uint(1), countResp.TargetsOnline)
require.Equal(t, uint(2), countResp.TargetsOffline)
// team selected
countResp = countTargetsResponse{}
s.DoJSON("POST", "/api/latest/fleet/targets/count", countTargetsRequest{Selected: fleet.HostTargets{TeamIDs: []uint{team.ID}}}, http.StatusOK, &countResp)
countResp = fleet.CountTargetsResponse{}
s.DoJSON("POST", "/api/latest/fleet/targets/count", fleet.CountTargetsRequest{Selected: fleet.HostTargets{TeamIDs: []uint{team.ID}}}, http.StatusOK, &countResp)
require.Equal(t, uint(1), countResp.TargetsCount)
require.Equal(t, uint(1), countResp.TargetsOnline)
require.Equal(t, uint(0), countResp.TargetsOffline)
// 'No team' selected
countResp = countTargetsResponse{}
countResp = fleet.CountTargetsResponse{}
s.DoJSON(
"POST", "/api/latest/fleet/targets/count", countTargetsRequest{Selected: fleet.HostTargets{TeamIDs: []uint{0}}},
"POST", "/api/latest/fleet/targets/count", fleet.CountTargetsRequest{Selected: fleet.HostTargets{TeamIDs: []uint{0}}},
http.StatusOK, &countResp,
)
assert.Equal(t, uint(2), countResp.TargetsCount)
@@ -9712,8 +9712,8 @@ func (s *integrationTestSuite) TestCountTargets() {
assert.Equal(t, uint(2), countResp.TargetsOffline)
// host id selected
countResp = countTargetsResponse{}
s.DoJSON("POST", "/api/latest/fleet/targets/count", countTargetsRequest{Selected: fleet.HostTargets{HostIDs: []uint{hosts[1].ID}}}, http.StatusOK, &countResp)
countResp = fleet.CountTargetsResponse{}
s.DoJSON("POST", "/api/latest/fleet/targets/count", fleet.CountTargetsRequest{Selected: fleet.HostTargets{HostIDs: []uint{hosts[1].ID}}}, http.StatusOK, &countResp)
require.Equal(t, uint(1), countResp.TargetsCount)
require.Equal(t, uint(0), countResp.TargetsOnline)
require.Equal(t, uint(1), countResp.TargetsOffline)
@@ -15075,12 +15075,12 @@ func (s *integrationTestSuite) TestSecretVariablesGitOps() {
s.setTokenForTest(t, "gitops1@example.com", test.GoodPassword)
// Empty request
req := createSecretVariablesRequest{}
var resp createSecretVariablesResponse
req := fleet.CreateSecretVariablesRequest{}
var resp fleet.CreateSecretVariablesResponse
s.DoJSON("PUT", "/api/latest/fleet/spec/secret_variables", req, http.StatusOK, &resp)
// Secret variable name too long
req = createSecretVariablesRequest{
req = fleet.CreateSecretVariablesRequest{
SecretVariables: []fleet.SecretVariable{
{
Name: strings.Repeat("a", 256),
@@ -15092,7 +15092,7 @@ func (s *integrationTestSuite) TestSecretVariablesGitOps() {
assertBodyContains(t, httpResp, "secret variable name is too long")
// Secret variable name empty
req = createSecretVariablesRequest{
req = fleet.CreateSecretVariablesRequest{
SecretVariables: []fleet.SecretVariable{
{
Name: " ",
@@ -15104,7 +15104,7 @@ func (s *integrationTestSuite) TestSecretVariablesGitOps() {
assertBodyContains(t, httpResp, "secret variable name cannot be empty")
validName := strings.Repeat("G", 255)
req = createSecretVariablesRequest{
req = fleet.CreateSecretVariablesRequest{
SecretVariables: []fleet.SecretVariable{
{
Name: "FLEET_SECRET_" + validName,
@@ -15134,8 +15134,8 @@ func (s *integrationTestSuite) TestSecretVariables() {
ctx := context.Background()
// Create a single secret variable.
var csvr createSecretVariableResponse
s.DoJSON("POST", "/api/latest/fleet/custom_variables", createSecretVariableRequest{
var csvr fleet.CreateSecretVariableResponse
s.DoJSON("POST", "/api/latest/fleet/custom_variables", fleet.CreateSecretVariableRequest{
Name: "NAME1",
Value: "value1",
}, http.StatusOK, &csvr)
@@ -15143,8 +15143,8 @@ func (s *integrationTestSuite) TestSecretVariables() {
require.NotZero(t, firstVariableID)
// List (no-filtering).
var lsvr listSecretVariablesResponse
s.DoJSON("GET", "/api/latest/fleet/custom_variables", listSecretVariablesRequest{}, http.StatusOK, &lsvr)
var lsvr fleet.ListSecretVariablesResponse
s.DoJSON("GET", "/api/latest/fleet/custom_variables", fleet.ListSecretVariablesRequest{}, http.StatusOK, &lsvr)
require.Equal(t, lsvr.Count, 1)
require.Len(t, lsvr.CustomVariables, 1)
require.NotZero(t, lsvr.CustomVariables[0].ID)
@@ -15160,21 +15160,21 @@ func (s *integrationTestSuite) TestSecretVariables() {
require.NotZero(t, secretVariables[0].UpdatedAt)
// Creating the same variable should fail with conflict.
s.DoJSON("POST", "/api/latest/fleet/custom_variables", createSecretVariableRequest{
s.DoJSON("POST", "/api/latest/fleet/custom_variables", fleet.CreateSecretVariableRequest{
Name: "NAME1",
Value: "value1",
}, http.StatusConflict, &csvr)
// Creating a variable with invalid name should fail with 422.
s.DoJSON("POST", "/api/latest/fleet/custom_variables", createSecretVariableRequest{
s.DoJSON("POST", "/api/latest/fleet/custom_variables", fleet.CreateSecretVariableRequest{
Name: "lowercase",
Value: "foobar",
}, http.StatusUnprocessableEntity, &csvr)
s.DoJSON("POST", "/api/latest/fleet/custom_variables", createSecretVariableRequest{
s.DoJSON("POST", "/api/latest/fleet/custom_variables", fleet.CreateSecretVariableRequest{
Name: "",
Value: "foobar",
}, http.StatusUnprocessableEntity, &csvr)
s.DoJSON("POST", "/api/latest/fleet/custom_variables", createSecretVariableRequest{
s.DoJSON("POST", "/api/latest/fleet/custom_variables", fleet.CreateSecretVariableRequest{
Name: strings.Repeat("HA ", 255/3+1),
Value: "foobar",
}, http.StatusUnprocessableEntity, &csvr)
@@ -15183,7 +15183,7 @@ func (s *integrationTestSuite) TestSecretVariables() {
defer func() {
testSetEmptyPrivateKey = false
}()
s.DoJSON("POST", "/api/latest/fleet/custom_variables", createSecretVariableRequest{
s.DoJSON("POST", "/api/latest/fleet/custom_variables", fleet.CreateSecretVariableRequest{
Name: "NAME2",
Value: "foobar",
}, http.StatusBadRequest, &csvr)
@@ -15191,13 +15191,13 @@ func (s *integrationTestSuite) TestSecretVariables() {
testSetEmptyPrivateKey = false
// Creating a variable with empty value should fail with 422.
s.DoJSON("POST", "/api/latest/fleet/custom_variables", createSecretVariableRequest{
s.DoJSON("POST", "/api/latest/fleet/custom_variables", fleet.CreateSecretVariableRequest{
Name: "ANOTHER_NAME",
Value: "",
}, http.StatusUnprocessableEntity, &csvr)
// Creating a second variable.
s.DoJSON("POST", "/api/latest/fleet/custom_variables", createSecretVariableRequest{
s.DoJSON("POST", "/api/latest/fleet/custom_variables", fleet.CreateSecretVariableRequest{
Name: "ANOTHER_NAME",
Value: "value2",
}, http.StatusOK, &csvr)
@@ -15205,7 +15205,7 @@ func (s *integrationTestSuite) TestSecretVariables() {
require.NotZero(t, secondVariableID)
// List (no-filtering) with pagination (first page).
lsvr = listSecretVariablesResponse{}
lsvr = fleet.ListSecretVariablesResponse{}
s.DoJSON("GET", "/api/latest/fleet/custom_variables", nil, http.StatusOK, &lsvr, "per_page", "1", "page", "0")
require.Equal(t, 2, lsvr.Count)
require.NotNil(t, lsvr.Meta)
@@ -15216,7 +15216,7 @@ func (s *integrationTestSuite) TestSecretVariables() {
require.Equal(t, "ANOTHER_NAME", lsvr.CustomVariables[0].Name)
require.NotZero(t, lsvr.CustomVariables[0].UpdatedAt)
// List (no-filtering) with pagination (second page).
lsvr = listSecretVariablesResponse{}
lsvr = fleet.ListSecretVariablesResponse{}
s.DoJSON("GET", "/api/latest/fleet/custom_variables", nil, http.StatusOK, &lsvr, "per_page", "1", "page", "1")
require.Equal(t, 2, lsvr.Count)
require.NotNil(t, lsvr.Meta)
@@ -15228,7 +15228,7 @@ func (s *integrationTestSuite) TestSecretVariables() {
require.NotZero(t, lsvr.CustomVariables[0].UpdatedAt)
// List (no-filtering) with pagination (one page, two secrets).
// Must be ordered alphabetically.
lsvr = listSecretVariablesResponse{}
lsvr = fleet.ListSecretVariablesResponse{}
s.DoJSON("GET", "/api/latest/fleet/custom_variables", nil, http.StatusOK, &lsvr, "per_page", "20", "page", "0")
require.Equal(t, 2, lsvr.Count)
require.NotNil(t, lsvr.Meta)
@@ -15243,14 +15243,14 @@ func (s *integrationTestSuite) TestSecretVariables() {
require.NotZero(t, lsvr.CustomVariables[1].UpdatedAt)
// Test deletion of non-existent ID
var dsvr deleteSecretVariableResponse
var dsvr fleet.DeleteSecretVariableResponse
s.DoJSON("DELETE", "/api/latest/fleet/custom_variables/999", nil, http.StatusNotFound, &dsvr)
s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/custom_variables/%d", firstVariableID), nil, http.StatusOK, &dsvr)
s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/custom_variables/%d", secondVariableID), nil, http.StatusOK, &dsvr)
// List after deletions should be empty.
lsvr = listSecretVariablesResponse{}
lsvr = fleet.ListSecretVariablesResponse{}
s.DoJSON("GET", "/api/latest/fleet/custom_variables", nil, http.StatusOK, &lsvr)
require.Equal(t, 0, lsvr.Count)
require.Empty(t, lsvr.CustomVariables)
@@ -15266,8 +15266,8 @@ func (s *integrationTestSuite) TestSecretVariablesInUse() {
require.NoError(t, err)
// Create a single secret variable.
var csvr createSecretVariableResponse
s.DoJSON("POST", "/api/latest/fleet/custom_variables", createSecretVariableRequest{
var csvr fleet.CreateSecretVariableResponse
s.DoJSON("POST", "/api/latest/fleet/custom_variables", fleet.CreateSecretVariableRequest{
Name: "NAME1",
Value: "value1",
}, http.StatusOK, &csvr)
@@ -15350,7 +15350,7 @@ func (s *integrationTestSuite) TestSecretVariablesInUse() {
require.NoError(t, err)
// Finally, delete now should work.
var dsvr deleteSecretVariableResponse
var dsvr fleet.DeleteSecretVariableResponse
s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/custom_variables/%d", firstVariableID), nil, http.StatusOK, &dsvr)
}
@@ -15359,8 +15359,8 @@ func (s *integrationTestSuite) TestSecretVariablesPermissions() {
ctx := context.Background()
// Create a single secret variable.
var csvr createSecretVariableResponse
s.DoJSON("POST", "/api/latest/fleet/custom_variables", createSecretVariableRequest{
var csvr fleet.CreateSecretVariableResponse
s.DoJSON("POST", "/api/latest/fleet/custom_variables", fleet.CreateSecretVariableRequest{
Name: "NAME1",
Value: "foobar",
}, http.StatusOK, &csvr)
@@ -15376,14 +15376,14 @@ func (s *integrationTestSuite) TestSecretVariablesPermissions() {
require.NoError(t, err)
s.setTokenForTest(t, "observer@example.com", test.GoodPassword)
s.DoJSON("POST", "/api/latest/fleet/custom_variables", createSecretVariableRequest{
s.DoJSON("POST", "/api/latest/fleet/custom_variables", fleet.CreateSecretVariableRequest{
Name: "NAME1",
Value: "foobar",
}, http.StatusForbidden, &csvr)
// List (no-filtering) should work for non-admins.
var lsvr listSecretVariablesResponse
s.DoJSON("GET", "/api/latest/fleet/custom_variables", listSecretVariablesRequest{}, http.StatusOK, &lsvr)
var lsvr fleet.ListSecretVariablesResponse
s.DoJSON("GET", "/api/latest/fleet/custom_variables", fleet.ListSecretVariablesRequest{}, http.StatusOK, &lsvr)
require.Equal(t, lsvr.Count, 1)
require.Len(t, lsvr.CustomVariables, 1)
require.NotZero(t, lsvr.CustomVariables[0].ID)
+10 -10
View File
@@ -7358,20 +7358,20 @@ func (s *integrationEnterpriseTestSuite) TestGitOpsUserActions() {
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/carves/%d", carveID), fleet.GetCarveRequest{}, http.StatusForbidden, &fleet.GetCarveResponse{})
// Attempt to search hosts, should fail.
s.DoJSON("POST", "/api/latest/fleet/targets", searchTargetsRequest{
s.DoJSON("POST", "/api/latest/fleet/targets", fleet.SearchTargetsRequest{
MatchQuery: "foo",
QueryID: &q1.ID,
}, http.StatusForbidden, &searchTargetsResponse{})
}, http.StatusForbidden, &fleet.SearchTargetsResponse{})
// Attempt to count target hosts, should fail.
s.DoJSON("POST", "/api/latest/fleet/targets/count", countTargetsRequest{
s.DoJSON("POST", "/api/latest/fleet/targets/count", fleet.CountTargetsRequest{
Selected: fleet.HostTargets{
HostIDs: []uint{h1.ID},
LabelIDs: []uint{clr.Label.ID},
TeamIDs: []uint{t1.ID},
},
QueryID: &q1.ID,
}, http.StatusForbidden, &countTargetsResponse{})
}, http.StatusForbidden, &fleet.CountTargetsResponse{})
//
// Start running permission tests with user gitops2 (which is a GitOps use for team t1).
@@ -7594,20 +7594,20 @@ func (s *integrationEnterpriseTestSuite) TestGitOpsUserActions() {
}, http.StatusForbidden, &teamResponse{})
// Attempt to search hosts, should fail.
s.DoJSON("POST", "/api/latest/fleet/targets", searchTargetsRequest{
s.DoJSON("POST", "/api/latest/fleet/targets", fleet.SearchTargetsRequest{
MatchQuery: "foo",
QueryID: &q1.ID,
}, http.StatusForbidden, &searchTargetsResponse{})
}, http.StatusForbidden, &fleet.SearchTargetsResponse{})
// Attempt to count target hosts, should fail.
s.DoJSON("POST", "/api/latest/fleet/targets/count", countTargetsRequest{
s.DoJSON("POST", "/api/latest/fleet/targets/count", fleet.CountTargetsRequest{
Selected: fleet.HostTargets{
HostIDs: []uint{h1.ID},
LabelIDs: []uint{clr.Label.ID},
TeamIDs: []uint{t1.ID},
},
QueryID: &q1.ID,
}, http.StatusForbidden, &countTargetsResponse{})
}, http.StatusForbidden, &fleet.CountTargetsResponse{})
// Listing software titles for the team it owns is allowed.
s.DoJSON("GET", "/api/latest/fleet/software/titles", listSoftwareTitlesRequest{}, http.StatusOK, &listSoftwareTitlesResponse{}, "team_id", fmt.Sprint(t1.ID))
@@ -7684,7 +7684,7 @@ func (s *integrationEnterpriseTestSuite) TestRunHostScript() {
// Upload a valid secret
secretValue := "abc123"
req := createSecretVariablesRequest{
req := fleet.CreateSecretVariablesRequest{
SecretVariables: []fleet.SecretVariable{
{
Name: "FLEET_SECRET_TEST_RUN_HOST_SCRIPT",
@@ -7692,7 +7692,7 @@ func (s *integrationEnterpriseTestSuite) TestRunHostScript() {
},
},
}
secretResp := createSecretVariablesResponse{}
secretResp := fleet.CreateSecretVariablesResponse{}
s.DoJSON("PUT", "/api/latest/fleet/spec/secret_variables", req, http.StatusOK, &secretResp)
// create a valid script execution request
+3 -3
View File
@@ -525,7 +525,7 @@ func (s *integrationMDMTestSuite) TestAppleDDMSecretVariables() {
require.Empty(t, resp.Profiles)
// Add secrets to server
req := createSecretVariablesRequest{
req := fleet.CreateSecretVariablesRequest{
SecretVariables: []fleet.SecretVariable{
{
Name: "FLEET_SECRET_BASH",
@@ -537,7 +537,7 @@ func (s *integrationMDMTestSuite) TestAppleDDMSecretVariables() {
},
},
}
secretResp := createSecretVariablesResponse{}
secretResp := fleet.CreateSecretVariablesResponse{}
s.DoJSON("PUT", "/api/latest/fleet/spec/secret_variables", req, http.StatusOK, &secretResp)
// Now real run
@@ -654,7 +654,7 @@ WHERE name = ?`
// Change the secrets.
myBash = "my.new.bash"
req = createSecretVariablesRequest{
req = fleet.CreateSecretVariablesRequest{
SecretVariables: []fleet.SecretVariable{
{
Name: "FLEET_SECRET_BASH",
@@ -243,7 +243,7 @@ func (s *integrationMDMTestSuite) TestAppleProfileManagement() {
secretIdentifier := "secret-identifier-1"
secretType := "secret.type.1"
secretProfile := string(mobileconfigForTest("NS1", "IS1"))
req := createSecretVariablesRequest{
req := fleet.CreateSecretVariablesRequest{
SecretVariables: []fleet.SecretVariable{
{
Name: "FLEET_SECRET_IDENTIFIER",
@@ -259,7 +259,7 @@ func (s *integrationMDMTestSuite) TestAppleProfileManagement() {
},
},
}
secretResp := createSecretVariablesResponse{}
secretResp := fleet.CreateSecretVariablesResponse{}
s.DoJSON("PUT", "/api/latest/fleet/spec/secret_variables", req, http.StatusOK, &secretResp)
// set new team profiles (delete + addition)
@@ -305,7 +305,7 @@ func (s *integrationMDMTestSuite) TestAppleProfileManagement() {
// Change the secret variable and upload the profiles again. We should see the profile with updated secret installed.
secretType = "new.secret.type.1"
req = createSecretVariablesRequest{
req = fleet.CreateSecretVariablesRequest{
SecretVariables: []fleet.SecretVariable{
{
Name: "FLEET_SECRET_IDENTIFIER",
@@ -379,7 +379,7 @@ func (s *integrationMDMTestSuite) TestAppleProfileManagement() {
// Change the secret variable and upload the profiles again. We should see the profile with updated secret installed.
secretType = "new2.secret.type.1"
req = createSecretVariablesRequest{
req = fleet.CreateSecretVariablesRequest{
SecretVariables: []fleet.SecretVariable{
{
Name: "FLEET_SECRET_IDENTIFIER",
@@ -6668,7 +6668,7 @@ func (s *integrationMDMTestSuite) testSecretVariablesUpload(newProfileBytes func
assertBodyContains(t, res, `Secret variable \"$FLEET_SECRET_BASH\" missing`)
// Add secret(s) to server
req := createSecretVariablesRequest{
req := fleet.CreateSecretVariablesRequest{
SecretVariables: []fleet.SecretVariable{
{
Name: "FLEET_SECRET_BASH",
@@ -6680,7 +6680,7 @@ func (s *integrationMDMTestSuite) testSecretVariablesUpload(newProfileBytes func
},
},
}
secretResp := createSecretVariablesResponse{}
secretResp := fleet.CreateSecretVariablesResponse{}
s.DoJSON("PUT", "/api/latest/fleet/spec/secret_variables", req, http.StatusOK, &secretResp)
res = s.DoRawWithHeaders("POST", "/api/latest/fleet/configuration_profiles", body.Bytes(), http.StatusOK, headers)
var resp newMDMConfigProfileResponse
+12 -12
View File
@@ -3979,7 +3979,7 @@ func (s *integrationMDMTestSuite) TestEnqueueMDMCommandWithSecret() {
// Load secret(s)
secretValue := "*abc123*"
req := createSecretVariablesRequest{
req := fleet.CreateSecretVariablesRequest{
SecretVariables: []fleet.SecretVariable{
{
Name: "FLEET_SECRET_VALUE",
@@ -3987,7 +3987,7 @@ func (s *integrationMDMTestSuite) TestEnqueueMDMCommandWithSecret() {
},
},
}
secretResp := createSecretVariablesResponse{}
secretResp := fleet.CreateSecretVariablesResponse{}
s.DoJSON("PUT", "/api/latest/fleet/spec/secret_variables", req, http.StatusOK, &secretResp)
// call with enrolled host UUID
@@ -9154,7 +9154,7 @@ func (s *integrationMDMTestSuite) TestWindowsMDMCommandWithSecret() {
orbitHost, d := createWindowsHostThenEnrollMDM(s.ds, s.server.URL, t)
secretValue := "abcd1234"
req := createSecretVariablesRequest{
req := fleet.CreateSecretVariablesRequest{
SecretVariables: []fleet.SecretVariable{
{
Name: "FLEET_SECRET_DATA",
@@ -9162,7 +9162,7 @@ func (s *integrationMDMTestSuite) TestWindowsMDMCommandWithSecret() {
},
},
}
secretResp := createSecretVariablesResponse{}
secretResp := fleet.CreateSecretVariablesResponse{}
s.DoJSON("PUT", "/api/latest/fleet/spec/secret_variables", req, http.StatusOK, &secretResp)
cmdOneUUID := uuid.New().String()
@@ -23005,20 +23005,20 @@ func (s *integrationMDMTestSuite) TestTechnicianPermissions() {
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/carves/%d", carveID), fleet.GetCarveRequest{}, http.StatusForbidden, &fleet.GetCarveResponse{})
// Attempt to search hosts, should allow.
s.DoJSON("POST", "/api/latest/fleet/targets", searchTargetsRequest{
s.DoJSON("POST", "/api/latest/fleet/targets", fleet.SearchTargetsRequest{
MatchQuery: "foo",
QueryID: &q1.ID,
}, http.StatusOK, &searchTargetsResponse{})
}, http.StatusOK, &fleet.SearchTargetsResponse{})
// Attempt to count target hosts, should allow.
s.DoJSON("POST", "/api/latest/fleet/targets/count", countTargetsRequest{
s.DoJSON("POST", "/api/latest/fleet/targets/count", fleet.CountTargetsRequest{
Selected: fleet.HostTargets{
HostIDs: []uint{h1.ID},
LabelIDs: []uint{clr.Label.ID},
TeamIDs: []uint{t1.ID},
},
QueryID: &q1.ID,
}, http.StatusOK, &countTargetsResponse{})
}, http.StatusOK, &fleet.CountTargetsResponse{})
checkDownloadResponse := func(t *testing.T, r *http.Response, expectedFilename string) {
require.Equal(t, "application/octet-stream", r.Header.Get("Content-Type"))
@@ -23356,20 +23356,20 @@ func (s *integrationMDMTestSuite) TestTechnicianPermissions() {
}, http.StatusForbidden, &teamResponse{})
// Attempt to search hosts, should allow.
s.DoJSON("POST", "/api/latest/fleet/targets", searchTargetsRequest{
s.DoJSON("POST", "/api/latest/fleet/targets", fleet.SearchTargetsRequest{
MatchQuery: "foo",
QueryID: &q1.ID,
}, http.StatusOK, &searchTargetsResponse{})
}, http.StatusOK, &fleet.SearchTargetsResponse{})
// Attempt to count target hosts, should allow.
s.DoJSON("POST", "/api/latest/fleet/targets/count", countTargetsRequest{
s.DoJSON("POST", "/api/latest/fleet/targets/count", fleet.CountTargetsRequest{
Selected: fleet.HostTargets{
HostIDs: []uint{h1.ID},
LabelIDs: []uint{clr.Label.ID},
TeamIDs: []uint{t1.ID},
},
QueryID: &q1.ID,
}, http.StatusOK, &countTargetsResponse{})
}, http.StatusOK, &fleet.CountTargetsResponse{})
// Attempt to download installer from t1, should allow.
tokenResp = getSoftwareInstallerTokenResponse{}
+9 -58
View File
@@ -19,21 +19,10 @@ const (
// Create secret variables (spec)
//////////////////////////////////////////////////////////////////////////////////
type createSecretVariablesRequest struct {
DryRun bool `json:"dry_run"`
SecretVariables []fleet.SecretVariable `json:"secrets"`
}
type createSecretVariablesResponse struct {
Err error `json:"error,omitempty"`
}
func (r createSecretVariablesResponse) Error() error { return r.Err }
func createSecretVariablesEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (fleet.Errorer, error) {
req := request.(*createSecretVariablesRequest)
req := request.(*fleet.CreateSecretVariablesRequest)
err := svc.CreateSecretVariables(ctx, req.SecretVariables, req.DryRun)
return createSecretVariablesResponse{Err: err}, nil
return fleet.CreateSecretVariablesResponse{Err: err}, nil
}
func (svc *Service) CreateSecretVariables(ctx context.Context, secretVariables []fleet.SecretVariable, dryRun bool) error {
@@ -77,27 +66,13 @@ func (svc *Service) CreateSecretVariables(ctx context.Context, secretVariables [
// Create secret variable
//////////////////////////////////////////////////////////////////////////////////
type createSecretVariableRequest struct {
Name string `json:"name"`
Value string `json:"value"`
}
type createSecretVariableResponse struct {
ID uint `json:"id"`
Name string `json:"name"`
Err error `json:"error,omitempty"`
}
func (r createSecretVariableResponse) Error() error { return r.Err }
func createSecretVariableEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (fleet.Errorer, error) {
req := request.(*createSecretVariableRequest)
req := request.(*fleet.CreateSecretVariableRequest)
id, err := svc.CreateSecretVariable(ctx, req.Name, req.Value)
if err != nil {
return createSecretVariableResponse{Err: err}, nil
return fleet.CreateSecretVariableResponse{Err: err}, nil
}
return createSecretVariableResponse{
return fleet.CreateSecretVariableResponse{
ID: id,
Name: req.Name,
}, nil
@@ -150,24 +125,10 @@ func (svc *Service) CreateSecretVariable(ctx context.Context, name string, value
// List secret variables
//////////////////////////////////////////////////////////////////////////////////
type listSecretVariablesRequest struct {
ListOptions fleet.ListOptions `url:"list_options"`
}
type listSecretVariablesResponse struct {
CustomVariables []fleet.SecretVariableIdentifier `json:"custom_variables"`
Meta *fleet.PaginationMetadata `json:"meta"`
Count int `json:"count"`
Err error `json:"error,omitempty"`
}
func (r listSecretVariablesResponse) Error() error { return r.Err }
func listSecretVariablesEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (fleet.Errorer, error) {
req := request.(*listSecretVariablesRequest)
req := request.(*fleet.ListSecretVariablesRequest)
secretVariables, meta, count, err := svc.ListSecretVariables(ctx, req.ListOptions)
return listSecretVariablesResponse{
return fleet.ListSecretVariablesResponse{
CustomVariables: secretVariables,
Meta: meta,
Count: count,
@@ -210,20 +171,10 @@ func (svc *Service) ListSecretVariables(
// Delete secret variable
//////////////////////////////////////////////////////////////////////////////////
type deleteSecretVariableRequest struct {
ID uint `url:"id"`
}
type deleteSecretVariableResponse struct {
Err error `json:"error,omitempty"`
}
func (r deleteSecretVariableResponse) Error() error { return r.Err }
func deleteSecretVariableEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (fleet.Errorer, error) {
req := request.(*deleteSecretVariableRequest)
req := request.(*fleet.DeleteSecretVariableRequest)
err := svc.DeleteSecretVariable(ctx, req.ID)
return deleteSecretVariableResponse{
return fleet.DeleteSecretVariableResponse{
Err: err,
}, nil
}
+12 -141
View File
@@ -2,8 +2,6 @@ package service
import (
"context"
"encoding/json"
"time"
"github.com/fleetdm/fleet/v4/server/contexts/viewer"
"github.com/fleetdm/fleet/v4/server/fleet"
@@ -13,131 +11,18 @@ import (
// Search Targets
////////////////////////////////////////////////////////////////////////////////
type searchTargetsRequest struct {
// MatchQuery is the query SQL
MatchQuery string `json:"query"`
// QueryID is the ID of a saved query to run (used to determine if this is a
// query that observers can run).
QueryID *uint `json:"query_id" renameto:"report_id"`
// Selected is the list of IDs that are already selected on the caller side
// (e.g. the UI), so those are IDs that will be omitted from the returned
// payload.
Selected fleet.HostTargets `json:"selected"`
}
type labelSearchResult struct {
*fleet.Label
DisplayText string `json:"display_text"`
Count int `json:"count"`
}
type teamSearchResult struct {
*fleet.Team
DisplayText string `json:"display_text"`
Count int `json:"count"`
}
func (t teamSearchResult) MarshalJSON() ([]byte, error) {
x := struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
Description string `json:"description"`
fleet.TeamConfig
UserCount int `json:"user_count"`
Users []fleet.TeamUser `json:"users,omitempty"`
HostCount int `json:"host_count"`
Hosts []fleet.HostResponse `json:"hosts,omitempty"`
Secrets []*fleet.EnrollSecret `json:"secrets,omitempty"`
DisplayText string `json:"display_text"`
Count int `json:"count"`
}{
ID: t.ID,
CreatedAt: t.CreatedAt,
Name: t.Name,
Description: t.Description,
TeamConfig: t.Config,
UserCount: t.UserCount,
Users: t.Users,
HostCount: t.HostCount,
Hosts: fleet.HostResponsesForHostsCheap(t.Hosts),
Secrets: t.Secrets,
DisplayText: t.DisplayText,
Count: t.Count,
}
return json.Marshal(x)
}
func (t *teamSearchResult) UnmarshalJSON(b []byte) error {
var x struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
Description string `json:"description"`
fleet.TeamConfig
UserCount int `json:"user_count"`
Users []fleet.TeamUser `json:"users,omitempty"`
HostCount int `json:"host_count"`
Hosts []fleet.Host `json:"hosts,omitempty"`
Secrets []*fleet.EnrollSecret `json:"secrets,omitempty"`
DisplayText string `json:"display_text"`
Count int `json:"count"`
}
if err := json.Unmarshal(b, &x); err != nil {
return err
}
*t = teamSearchResult{
Team: &fleet.Team{
ID: x.ID,
CreatedAt: x.CreatedAt,
Name: x.Name,
Description: x.Description,
Config: x.TeamConfig,
UserCount: x.UserCount,
Users: x.Users,
HostCount: x.HostCount,
Hosts: x.Hosts,
Secrets: x.Secrets,
},
DisplayText: x.DisplayText,
Count: x.Count,
}
return nil
}
type targetsData struct {
Hosts []*fleet.HostResponse `json:"hosts"`
Labels []labelSearchResult `json:"labels"`
Teams []teamSearchResult `json:"teams" renameto:"fleets"`
}
type searchTargetsResponse struct {
Targets *targetsData `json:"targets,omitempty"`
TargetsCount uint `json:"targets_count"`
TargetsOnline uint `json:"targets_online"`
TargetsOffline uint `json:"targets_offline"`
TargetsMissingInAction uint `json:"targets_missing_in_action"`
Err error `json:"error,omitempty"`
}
func (r searchTargetsResponse) Error() error { return r.Err }
func searchTargetsEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (fleet.Errorer, error) {
req := request.(*searchTargetsRequest)
req := request.(*fleet.SearchTargetsRequest)
results, err := svc.SearchTargets(ctx, req.MatchQuery, req.QueryID, req.Selected)
if err != nil {
return searchTargetsResponse{Err: err}, nil
return fleet.SearchTargetsResponse{Err: err}, nil
}
targets := &targetsData{
targets := &fleet.TargetsData{
Hosts: []*fleet.HostResponse{},
Labels: []labelSearchResult{},
Teams: []teamSearchResult{},
Labels: []fleet.LabelSearchResult{},
Teams: []fleet.TeamSearchResult{},
}
for _, host := range results.Hosts {
@@ -146,7 +31,7 @@ func searchTargetsEndpoint(ctx context.Context, request interface{}, svc fleet.S
for _, label := range results.Labels {
targets.Labels = append(targets.Labels,
labelSearchResult{
fleet.LabelSearchResult{
Label: label,
DisplayText: label.Name,
Count: label.HostCount,
@@ -156,7 +41,7 @@ func searchTargetsEndpoint(ctx context.Context, request interface{}, svc fleet.S
for _, team := range results.Teams {
targets.Teams = append(targets.Teams,
teamSearchResult{
fleet.TeamSearchResult{
Team: team,
DisplayText: team.Name,
Count: team.HostCount,
@@ -166,10 +51,10 @@ func searchTargetsEndpoint(ctx context.Context, request interface{}, svc fleet.S
metrics, err := svc.CountHostsInTargets(ctx, req.QueryID, req.Selected)
if err != nil {
return searchTargetsResponse{Err: err}, nil
return fleet.SearchTargetsResponse{Err: err}, nil
}
return searchTargetsResponse{
return fleet.SearchTargetsResponse{
Targets: targets,
TargetsCount: metrics.TotalHosts,
TargetsOnline: metrics.OnlineHosts,
@@ -258,29 +143,15 @@ func (svc *Service) CountHostsInTargets(ctx context.Context, queryID *uint, targ
return &metrics, nil
}
type countTargetsRequest struct {
Selected fleet.HostTargets `json:"selected"`
QueryID *uint `json:"query_id" renameto:"report_id"`
}
type countTargetsResponse struct {
TargetsCount uint `json:"targets_count"`
TargetsOnline uint `json:"targets_online"`
TargetsOffline uint `json:"targets_offline"`
Err error `json:"error,omitempty"`
}
func (r countTargetsResponse) Error() error { return r.Err }
func countTargetsEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (fleet.Errorer, error) {
req := request.(*countTargetsRequest)
req := request.(*fleet.CountTargetsRequest)
counts, err := svc.CountHostsInTargets(ctx, req.QueryID, req.Selected)
if err != nil {
return searchTargetsResponse{Err: err}, nil
return fleet.CountTargetsResponse{Err: err}, nil
}
return countTargetsResponse{
return fleet.CountTargetsResponse{
TargetsCount: counts.TotalHosts,
TargetsOnline: counts.OnlineHosts,
TargetsOffline: counts.OfflineHosts,