fleet/server/service/client_teams.go
Lucas Manuel Rodriguez 8d664bd456
Make software batch endpoint asynchronous (#22258)
#22069

API changes: https://github.com/fleetdm/fleet/pull/22259

QAd by applying 10 pieces of software on a team, which took 3+ minutes
in total (which, before these changes was timing out at 100s.)

With this approach, a GitOps CI run timing out might leave the
background process running (which will eventually be applied to the
database). The team discussed and agreed that we can fix this edge case
later.

- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/Committing-Changes.md#changes-files)
for more information.
- [X] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)
- [X] Added/updated tests
- [X] Manual QA for all new/changed functionality
2024-09-20 11:55:47 -03:00

113 lines
4 KiB
Go

package service
import (
"encoding/json"
"fmt"
"net/url"
"strconv"
"github.com/fleetdm/fleet/v4/server/fleet"
)
// ListTeams retrieves the list of teams.
func (c *Client) ListTeams(query string) ([]fleet.Team, error) {
verb, path := "GET", "/api/latest/fleet/teams"
var responseBody listTeamsResponse
err := c.authenticatedRequestWithQuery(nil, verb, path, &responseBody, query)
if err != nil {
return nil, err
}
return responseBody.Teams, nil
}
// CreateTeam creates a new team.
func (c *Client) CreateTeam(teamPayload fleet.TeamPayload) (*fleet.Team, error) {
req := createTeamRequest{
TeamPayload: teamPayload,
}
verb, path := "POST", "/api/latest/fleet/teams"
var responseBody teamResponse
err := c.authenticatedRequest(req, verb, path, &responseBody)
if err != nil {
return nil, err
}
return responseBody.Team, nil
}
func (c *Client) GetTeam(teamID uint) (*fleet.Team, error) {
verb, path := "GET", fmt.Sprintf("/api/latest/fleet/teams/%d", teamID)
var responseBody getTeamResponse
if err := c.authenticatedRequest(getTeamRequest{}, verb, path, &responseBody); err != nil {
return nil, err
}
return responseBody.Team, nil
}
// DeleteTeam deletes a team.
func (c *Client) DeleteTeam(teamID uint) error {
verb, path := "DELETE", "/api/latest/fleet/teams/"+strconv.FormatUint(uint64(teamID), 10)
var responseBody deleteTeamResponse
return c.authenticatedRequest(nil, verb, path, &responseBody)
}
// ApplyTeams sends the list of Teams to be applied to the
// Fleet instance.
func (c *Client) ApplyTeams(specs []json.RawMessage, opts fleet.ApplyTeamSpecOptions) (map[string]uint, error) {
verb, path := "POST", "/api/latest/fleet/spec/teams"
var responseBody applyTeamSpecsResponse
params := map[string]interface{}{"specs": specs}
if opts.DryRun && opts.DryRunAssumptions != nil {
params["dry_run_assumptions"] = opts.DryRunAssumptions
}
err := c.authenticatedRequestWithQuery(params, verb, path, &responseBody, opts.RawQuery())
if err != nil {
return nil, err
}
return responseBody.TeamIDsByName, nil
}
// ApplyTeamProfiles sends the list of profiles to be applied for the specified
// team.
func (c *Client) ApplyTeamProfiles(tmName string, profiles []fleet.MDMProfileBatchPayload, opts fleet.ApplyTeamSpecOptions) error {
verb, path := "POST", "/api/latest/fleet/mdm/profiles/batch"
query, err := url.ParseQuery(opts.RawQuery())
if err != nil {
return err
}
query.Add("team_name", tmName)
if opts.DryRunAssumptions != nil && opts.DryRunAssumptions.WindowsEnabledAndConfigured.Valid {
query.Add("assume_enabled", strconv.FormatBool(opts.DryRunAssumptions.WindowsEnabledAndConfigured.Value))
}
return c.authenticatedRequestWithQuery(map[string]interface{}{"profiles": profiles}, verb, path, nil, query.Encode())
}
// ApplyTeamScripts sends the list of scripts to be applied for the specified
// team.
func (c *Client) ApplyTeamScripts(tmName string, scripts []fleet.ScriptPayload, opts fleet.ApplySpecOptions) error {
verb, path := "POST", "/api/latest/fleet/scripts/batch"
query, err := url.ParseQuery(opts.RawQuery())
if err != nil {
return err
}
query.Add("team_name", tmName)
return c.authenticatedRequestWithQuery(map[string]interface{}{"scripts": scripts}, verb, path, nil, query.Encode())
}
func (c *Client) ApplyTeamSoftwareInstallers(tmName string, softwareInstallers []fleet.SoftwareInstallerPayload, opts fleet.ApplySpecOptions) ([]fleet.SoftwarePackageResponse, error) {
query, err := url.ParseQuery(opts.RawQuery())
if err != nil {
return nil, err
}
query.Add("team_name", tmName)
return c.applySoftwareInstallers(softwareInstallers, query, opts.DryRun)
}
func (c *Client) ApplyTeamAppStoreAppsAssociation(tmName string, vppBatchPayload []fleet.VPPBatchPayload, opts fleet.ApplySpecOptions) error {
verb, path := "POST", "/api/latest/fleet/software/app_store_apps/batch"
query, err := url.ParseQuery(opts.RawQuery())
if err != nil {
return err
}
query.Add("team_name", tmName)
return c.authenticatedRequestWithQuery(map[string]interface{}{"app_store_apps": vppBatchPayload}, verb, path, nil, query.Encode())
}