<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #36807 cherry-pick for https://github.com/fleetdm/fleet/pull/37062
This commit is contained in:
@@ -67,6 +67,9 @@ func TestHostsTransferByHosts(t *testing.T) {
|
||||
ds.ListHostsLiteByIDsFunc = func(ctx context.Context, ids []uint) ([]*fleet.Host, error) {
|
||||
return nil, nil
|
||||
}
|
||||
ds.ListMDMAndroidUUIDsToHostIDsFunc = func(ctx context.Context, hostIDs []uint) (map[string]uint, error) {
|
||||
return map[string]uint{}, nil
|
||||
}
|
||||
|
||||
assert.Equal(t, "", RunAppForTest(t, []string{"hosts", "transfer", "--team", "team1", "--hosts", "host1"}))
|
||||
assert.True(t, ds.AddHostsToTeamFuncInvoked)
|
||||
|
||||
@@ -1903,3 +1903,40 @@ func (ds *Datastore) updateAndroidAppConfigurationTx(ctx context.Context, tx sql
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) ListMDMAndroidUUIDsToHostIDs(ctx context.Context, hostIDs []uint) (map[string]uint, error) {
|
||||
if len(hostIDs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
stmt := `
|
||||
SELECT
|
||||
h.id AS id, h.uuid AS uuid
|
||||
FROM
|
||||
hosts h
|
||||
JOIN android_devices ad ON ad.host_id = h.id
|
||||
WHERE
|
||||
h.id IN (?) AND
|
||||
h.platform = 'android'
|
||||
`
|
||||
|
||||
stmt, args, err := sqlx.In(stmt, hostIDs)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "prepare statement arguments")
|
||||
}
|
||||
|
||||
var rows []struct {
|
||||
ID uint `db:"id"`
|
||||
UUID string `db:"uuid"`
|
||||
}
|
||||
if err := sqlx.SelectContext(ctx, ds.reader(ctx), &rows, stmt, args...); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "list mdm android uuids to host ids")
|
||||
}
|
||||
|
||||
results := make(map[string]uint, len(rows))
|
||||
for _, r := range rows {
|
||||
results[r.UUID] = r.ID
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
@@ -3113,17 +3113,18 @@ func (ds *Datastore) getIncludedHostUUIDMapForSoftware(ctx context.Context, tx s
|
||||
FROM
|
||||
hosts h
|
||||
JOIN android_devices ad ON ad.enterprise_specific_id = h.uuid
|
||||
JOIN vpp_apps_teams vat ON vat.team_id <=> h.team_id AND vat.id = ?
|
||||
WHERE
|
||||
EXISTS (%s)
|
||||
AND platform = 'android'
|
||||
AND h.platform = 'android'
|
||||
`, filter)
|
||||
|
||||
var queryResults []struct {
|
||||
UUID string `db:"uuid"`
|
||||
AppliedPolicyID *string `db:"applied_policy_id"`
|
||||
}
|
||||
if err := sqlx.SelectContext(ctx, tx, &queryResults, stmt, softwareID, softwareID, softwareID); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "listing host uuids included in software scope")
|
||||
if err := sqlx.SelectContext(ctx, tx, &queryResults, stmt, softwareID, softwareID, softwareID, softwareID); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "listing hosts included in software scope")
|
||||
}
|
||||
|
||||
res := make(map[string]string, len(queryResults))
|
||||
|
||||
@@ -633,7 +633,7 @@ func (ds *Datastore) InsertVPPAppWithTeam(ctx context.Context, app *fleet.VPPApp
|
||||
}
|
||||
}
|
||||
|
||||
if vppToken != nil {
|
||||
if vppToken != nil && app.Platform != fleet.AndroidPlatform {
|
||||
vppTokenID = &vppToken.ID
|
||||
}
|
||||
|
||||
@@ -2250,6 +2250,7 @@ FROM (
|
||||
0 AS count_host_updated_after_labels,
|
||||
vpp_apps_teams.adam_id AS installable_id
|
||||
FROM vpp_apps_teams
|
||||
JOIN hosts ON hosts.id = ? AND hosts.team_id <=> vpp_apps_teams.team_id
|
||||
LEFT JOIN vpp_app_team_labels ON vpp_app_team_labels.vpp_app_team_id = vpp_apps_teams.id
|
||||
WHERE vpp_app_team_labels.id IS NULL AND vpp_apps_teams.platform = 'android'
|
||||
|
||||
@@ -2264,6 +2265,7 @@ FROM (
|
||||
FROM
|
||||
vpp_app_team_labels vatl
|
||||
LEFT JOIN vpp_apps_teams ON vpp_apps_teams.id = vatl.vpp_app_team_id
|
||||
JOIN hosts ON hosts.id = ? AND hosts.team_id <=> vpp_apps_teams.team_id
|
||||
LEFT OUTER JOIN label_membership lm ON lm.label_id = vatl.label_id
|
||||
AND lm.host_id = ?
|
||||
WHERE vatl.exclude = 0 AND vpp_apps_teams.platform = 'android'
|
||||
@@ -2300,6 +2302,7 @@ FROM (
|
||||
FROM
|
||||
vpp_app_team_labels vatl
|
||||
LEFT JOIN vpp_apps_teams ON vpp_apps_teams.id = vatl.vpp_app_team_id
|
||||
JOIN hosts ON hosts.id = ? AND hosts.team_id <=> vpp_apps_teams.team_id
|
||||
LEFT OUTER JOIN labels lbl ON lbl.id = vatl.label_id
|
||||
LEFT OUTER JOIN label_membership lm ON lm.label_id = vatl.label_id
|
||||
AND lm.host_id = ?
|
||||
@@ -2311,7 +2314,7 @@ FROM (
|
||||
AND count_host_labels = 0) t;
|
||||
`
|
||||
|
||||
err = sqlx.SelectContext(ctx, ds.reader(ctx), &applicationIDs, stmt, hostID, hostID, hostID)
|
||||
err = sqlx.SelectContext(ctx, ds.reader(ctx), &applicationIDs, stmt, hostID, hostID, hostID, hostID, hostID, hostID)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "get in android apps in scope for host")
|
||||
}
|
||||
|
||||
@@ -2408,6 +2408,8 @@ type Datastore interface {
|
||||
// DeleteAndroidAppConfiguration removes an Android app configuration.
|
||||
DeleteAndroidAppConfiguration(ctx context.Context, adamID string, globalOrTeamID uint) error
|
||||
|
||||
ListMDMAndroidUUIDsToHostIDs(ctx context.Context, hostIDs []uint) (map[string]uint, error)
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
// SCIM
|
||||
|
||||
|
||||
@@ -76,3 +76,5 @@ type MDMAndroidPolicyRequest struct {
|
||||
AppliedPolicyVersion sql.Null[int64] `db:"applied_policy_version"`
|
||||
PolicyVersion sql.Null[int64] `db:"policy_version"`
|
||||
}
|
||||
|
||||
const AppStatusAvailable = "AVAILABLE"
|
||||
|
||||
@@ -1567,6 +1567,8 @@ type UpdateAndroidAppConfigurationFunc func(ctx context.Context, config *fleet.A
|
||||
|
||||
type DeleteAndroidAppConfigurationFunc func(ctx context.Context, adamID string, globalOrTeamID uint) error
|
||||
|
||||
type ListMDMAndroidUUIDsToHostIDsFunc func(ctx context.Context, hostIDs []uint) (map[string]uint, error)
|
||||
|
||||
type CreateScimUserFunc func(ctx context.Context, user *fleet.ScimUser) (uint, error)
|
||||
|
||||
type ScimUserByIDFunc func(ctx context.Context, id uint) (*fleet.ScimUser, error)
|
||||
@@ -4014,6 +4016,9 @@ type DataStore struct {
|
||||
DeleteAndroidAppConfigurationFunc DeleteAndroidAppConfigurationFunc
|
||||
DeleteAndroidAppConfigurationFuncInvoked bool
|
||||
|
||||
ListMDMAndroidUUIDsToHostIDsFunc ListMDMAndroidUUIDsToHostIDsFunc
|
||||
ListMDMAndroidUUIDsToHostIDsFuncInvoked bool
|
||||
|
||||
CreateScimUserFunc CreateScimUserFunc
|
||||
CreateScimUserFuncInvoked bool
|
||||
|
||||
@@ -9616,6 +9621,13 @@ func (s *DataStore) DeleteAndroidAppConfiguration(ctx context.Context, adamID st
|
||||
return s.DeleteAndroidAppConfigurationFunc(ctx, adamID, globalOrTeamID)
|
||||
}
|
||||
|
||||
func (s *DataStore) ListMDMAndroidUUIDsToHostIDs(ctx context.Context, hostIDs []uint) (map[string]uint, error) {
|
||||
s.mu.Lock()
|
||||
s.ListMDMAndroidUUIDsToHostIDsFuncInvoked = true
|
||||
s.mu.Unlock()
|
||||
return s.ListMDMAndroidUUIDsToHostIDsFunc(ctx, hostIDs)
|
||||
}
|
||||
|
||||
func (s *DataStore) CreateScimUser(ctx context.Context, user *fleet.ScimUser) (uint, error) {
|
||||
s.mu.Lock()
|
||||
s.CreateScimUserFuncInvoked = true
|
||||
|
||||
@@ -1109,6 +1109,23 @@ func (svc *Service) AddHostsToTeam(ctx context.Context, teamID *uint, hostIDs []
|
||||
}
|
||||
}
|
||||
|
||||
// If there are any Android hosts, update their available apps.
|
||||
androidUUIDs, err := svc.ds.ListMDMAndroidUUIDsToHostIDs(ctx, hostIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(androidUUIDs) > 0 {
|
||||
enterprise, err := svc.ds.GetEnterprise(ctx)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "get android enterprise")
|
||||
}
|
||||
|
||||
if err := worker.QueueBulkSetAndroidAppsAvailableForHosts(ctx, svc.ds, svc.logger, androidUUIDs, enterprise.Name()); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "queue bulk set available android apps for hosts job")
|
||||
}
|
||||
}
|
||||
|
||||
return svc.createTransferredHostsActivity(ctx, teamID, hostIDs, nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -847,6 +847,9 @@ func TestHostAuth(t *testing.T) {
|
||||
ds.IsHostDiskEncryptionKeyArchivedFunc = func(ctx context.Context, hostID uint) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
ds.ListMDMAndroidUUIDsToHostIDsFunc = func(ctx context.Context, hostIDs []uint) (map[string]uint, error) {
|
||||
return map[string]uint{}, nil
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -91,8 +92,25 @@ func (s *integrationMDMTestSuite) TestAndroidAppsSelfService() {
|
||||
)
|
||||
s.Assert().Contains(extractServerErrorText(r.Body), "Couldn't add software. The application ID isn't available in Play Store. Please find ID on the Play Store and try again.")
|
||||
|
||||
amapiConfig := struct {
|
||||
AppIDsToNames map[string]string
|
||||
EnterprisesPoliciesPatchValidator func(policyName string, policy *androidmanagement.Policy, opts androidmgmt.PoliciesPatchOpts)
|
||||
}{
|
||||
AppIDsToNames: map[string]string{},
|
||||
EnterprisesPoliciesPatchValidator: func(policyName string, policy *androidmanagement.Policy, opts androidmgmt.PoliciesPatchOpts) {},
|
||||
}
|
||||
|
||||
s.androidAPIClient.EnterprisesApplicationsFunc = func(ctx context.Context, enterpriseName string, packageName string) (*androidmanagement.Application, error) {
|
||||
return &androidmanagement.Application{IconUrl: "https://example.com/1.jpg", Title: "Test App"}, nil
|
||||
title := amapiConfig.AppIDsToNames[packageName]
|
||||
|
||||
return &androidmanagement.Application{IconUrl: "https://example.com/1.jpg", Title: title}, nil
|
||||
}
|
||||
|
||||
s.androidAPIClient.EnterprisesPoliciesPatchFunc = func(ctx context.Context, policyName string, policy *androidmanagement.Policy, opts androidmgmt.PoliciesPatchOpts) (*androidmanagement.Policy, error) {
|
||||
|
||||
amapiConfig.EnterprisesPoliciesPatchValidator(policyName, policy, opts)
|
||||
|
||||
return &androidmanagement.Policy{}, nil
|
||||
}
|
||||
|
||||
// Valid application ID format, but wrong platform specified: should fail
|
||||
@@ -193,6 +211,7 @@ func (s *integrationMDMTestSuite) TestAndroidAppsSelfService() {
|
||||
|
||||
// Should have hit the android API endpoint
|
||||
s.Assert().True(s.androidAPIClient.EnterprisesPoliciesModifyPolicyApplicationsFuncInvoked)
|
||||
s.androidAPIClient.EnterprisesPoliciesModifyPolicyApplicationsFuncInvoked = false
|
||||
|
||||
s.DoJSON(
|
||||
"PATCH",
|
||||
@@ -211,7 +230,108 @@ func (s *integrationMDMTestSuite) TestAndroidAppsSelfService() {
|
||||
return nil
|
||||
})
|
||||
|
||||
// Test Android app configurations
|
||||
// Add some apps to a different team. They shouldn't be sent to our existing host
|
||||
var newTeamResp teamResponse
|
||||
s.DoJSON("POST", "/api/latest/fleet/teams", &createTeamRequest{TeamPayload: fleet.TeamPayload{Name: ptr.String("Team 1")}}, http.StatusOK, &newTeamResp)
|
||||
team := newTeamResp.Team
|
||||
// Add Android app
|
||||
androidAppNewTeam := &fleet.VPPApp{
|
||||
VPPAppTeam: fleet.VPPAppTeam{
|
||||
VPPAppID: fleet.VPPAppID{
|
||||
AdamID: "com.my.cool.app",
|
||||
Platform: fleet.AndroidPlatform,
|
||||
},
|
||||
},
|
||||
Name: "My cool app",
|
||||
BundleIdentifier: "com.my.cool.app",
|
||||
IconURL: "https://example.com/images/3",
|
||||
}
|
||||
s.DoJSON(
|
||||
"POST",
|
||||
"/api/latest/fleet/software/app_store_apps",
|
||||
&addAppStoreAppRequest{AppStoreID: androidAppNewTeam.AdamID, Platform: fleet.AndroidPlatform, TeamID: &team.ID},
|
||||
http.StatusOK,
|
||||
&addAppResp,
|
||||
)
|
||||
|
||||
// New app should not show up in "No team" library
|
||||
s.DoJSON("GET", "/api/latest/fleet/software/titles", nil, http.StatusOK, &listSWTitles, "team_id", fmt.Sprint(0))
|
||||
s.Assert().Len(listSWTitles.SoftwareTitles, 1)
|
||||
s.Assert().Equal(androidApp.AdamID, listSWTitles.SoftwareTitles[0].AppStoreApp.AppStoreID) // just the app we had before
|
||||
s.Assert().Empty(listSWTitles.SoftwareTitles[0].AppStoreApp.Version)
|
||||
|
||||
// New app SHOULD show up in our new team library
|
||||
s.DoJSON("GET", "/api/latest/fleet/software/titles", nil, http.StatusOK, &listSWTitles, "team_id", fmt.Sprint(team.ID))
|
||||
s.Assert().Len(listSWTitles.SoftwareTitles, 1)
|
||||
s.Assert().Equal(androidAppNewTeam.AdamID, listSWTitles.SoftwareTitles[0].AppStoreApp.AppStoreID)
|
||||
s.Assert().Empty(listSWTitles.SoftwareTitles[0].AppStoreApp.Version)
|
||||
|
||||
androidAppNewTeam2 := &fleet.VPPApp{
|
||||
VPPAppTeam: fleet.VPPAppTeam{
|
||||
VPPAppID: fleet.VPPAppID{
|
||||
AdamID: "com.my.cool.app.two",
|
||||
Platform: fleet.AndroidPlatform,
|
||||
},
|
||||
},
|
||||
Name: "My cool app 2",
|
||||
BundleIdentifier: "com.my.cool.app.two",
|
||||
IconURL: "https://example.com/images/4",
|
||||
}
|
||||
|
||||
amapiConfig.AppIDsToNames[androidAppNewTeam2.AdamID] = androidAppNewTeam2.Name
|
||||
|
||||
s.DoJSON(
|
||||
"POST",
|
||||
"/api/latest/fleet/software/app_store_apps",
|
||||
&addAppStoreAppRequest{AppStoreID: androidAppNewTeam2.AdamID, Platform: fleet.AndroidPlatform, TeamID: &team.ID},
|
||||
http.StatusOK,
|
||||
&addAppResp,
|
||||
)
|
||||
|
||||
s.DoJSON("GET", "/api/latest/fleet/software/titles", nil, http.StatusOK, &listSWTitles, "team_id", fmt.Sprint(team.ID))
|
||||
s.Assert().Len(listSWTitles.SoftwareTitles, 2)
|
||||
s.Assert().True(slices.ContainsFunc(listSWTitles.SoftwareTitles, func(t fleet.SoftwareTitleListResult) bool {
|
||||
return t.AppStoreApp.AppStoreID == androidAppNewTeam.AdamID || t.AppStoreApp.AppStoreID == androidAppNewTeam2.AdamID
|
||||
}))
|
||||
|
||||
s.lastActivityMatches(fleet.ActivityAddedAppStoreApp{}.ActivityName(),
|
||||
fmt.Sprintf(`{"team_name": "%s", "software_title": "%s", "software_title_id": %d, "app_store_id": "%s", "team_id": %s, "platform": "%s", "self_service": true}`,
|
||||
team.Name, androidAppNewTeam2.Name, addAppResp.TitleID, androidAppNewTeam2.AdamID, fmt.Sprint(team.ID), androidAppNewTeam2.Platform), 0)
|
||||
|
||||
s.androidAPIClient.EnterprisesPoliciesPatchFuncInvoked = false
|
||||
s.runWorkerUntilDone()
|
||||
// We shouldn't have hit the AMAPI, since there are no hosts in the team
|
||||
s.Assert().False(s.androidAPIClient.EnterprisesPoliciesModifyPolicyApplicationsFuncInvoked)
|
||||
s.Assert().False(s.androidAPIClient.EnterprisesPoliciesPatchFuncInvoked)
|
||||
|
||||
amapiConfig.EnterprisesPoliciesPatchValidator = func(policyName string, policy *androidmanagement.Policy, opts androidmgmt.PoliciesPatchOpts) {
|
||||
var appIDs []string
|
||||
for _, a := range policy.Applications {
|
||||
appIDs = append(appIDs, a.PackageName)
|
||||
}
|
||||
|
||||
s.Assert().ElementsMatch(appIDs, []string{androidAppNewTeam.AdamID, androidAppNewTeam2.AdamID})
|
||||
s.Assert().Contains(policyName, host1.UUID)
|
||||
}
|
||||
|
||||
// Transfer a host to the team
|
||||
s.DoJSON("POST", "/api/latest/fleet/hosts/transfer", addHostsToTeamRequest{
|
||||
TeamID: &team.ID,
|
||||
HostIDs: []uint{host1.ID},
|
||||
}, http.StatusOK, &addHostsToTeamResponse{})
|
||||
|
||||
s.runWorkerUntilDone()
|
||||
s.Assert().True(s.androidAPIClient.EnterprisesPoliciesPatchFuncInvoked)
|
||||
|
||||
// Transfer host back to "No team"
|
||||
s.DoJSON("POST", "/api/latest/fleet/hosts/transfer", addHostsToTeamRequest{
|
||||
TeamID: nil,
|
||||
HostIDs: []uint{host1.ID},
|
||||
}, http.StatusOK, &addHostsToTeamResponse{})
|
||||
|
||||
// =========================================
|
||||
// Android app configurations
|
||||
// =========================================
|
||||
|
||||
// Title with no configuration should omit it from response
|
||||
var getAppResp map[string]any
|
||||
@@ -236,6 +356,8 @@ func (s *integrationMDMTestSuite) TestAndroidAppsSelfService() {
|
||||
IconURL: "https://example.com/images/2",
|
||||
}
|
||||
|
||||
amapiConfig.AppIDsToNames[androidAppWithConfig.AdamID] = androidAppWithConfig.Name
|
||||
|
||||
// Add Android app
|
||||
var appWithConfigResp addAppStoreAppResponse
|
||||
s.DoJSON(
|
||||
@@ -253,7 +375,7 @@ func (s *integrationMDMTestSuite) TestAndroidAppsSelfService() {
|
||||
// Verify that activity includes configuration
|
||||
s.lastActivityMatches(fleet.ActivityAddedAppStoreApp{}.ActivityName(),
|
||||
fmt.Sprintf(`{"team_name": "%s", "software_title": "%s", "software_title_id": %d, "app_store_id": "%s", "team_id": %s, "platform": "%s", "self_service": true,"configuration": %s}`,
|
||||
"", "Test App", appWithConfigResp.TitleID, androidAppWithConfig.AdamID, "null", androidAppWithConfig.Platform, androidAppWithConfig.Configuration), 0)
|
||||
"", androidAppWithConfig.Name, appWithConfigResp.TitleID, androidAppWithConfig.AdamID, "null", androidAppWithConfig.Platform, androidAppWithConfig.Configuration), 0)
|
||||
|
||||
// Should see it in host software library
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", host1.ID), nil, http.StatusOK, &getHostSw, "available_for_install", "true")
|
||||
@@ -294,7 +416,7 @@ func (s *integrationMDMTestSuite) TestAndroidAppsSelfService() {
|
||||
// Verify that configuration changed and last activity is correct
|
||||
s.lastActivityMatches(fleet.ActivityEditedAppStoreApp{}.ActivityName(),
|
||||
fmt.Sprintf(`{"team_name": "%s", "software_title": "%s", "software_icon_url":"https://example.com/1.jpg", "software_title_id": %d, "app_store_id": "%s", "team_id": %s, "software_display_name":"", "platform": "%s", "self_service": true,"configuration": %s}`,
|
||||
"", "Test App", appWithConfigResp.TitleID, androidAppWithConfig.AdamID, "null", androidAppWithConfig.Platform, newConfig), 0)
|
||||
"", androidAppWithConfig.Name, appWithConfigResp.TitleID, androidAppWithConfig.AdamID, "null", androidAppWithConfig.Platform, newConfig), 0)
|
||||
}
|
||||
|
||||
func (s *integrationMDMTestSuite) TestAndroidSetupExperienceSoftware() {
|
||||
|
||||
@@ -31,10 +31,11 @@ func (v *SoftwareWorker) Name() string {
|
||||
}
|
||||
|
||||
const (
|
||||
makeAndroidAppsAvailableForHostTask SoftwareWorkerTask = "make_android_apps_available_for_host"
|
||||
makeAndroidAppAvailableTask SoftwareWorkerTask = "make_android_app_available"
|
||||
runAndroidSetupExperienceTask SoftwareWorkerTask = "run_android_setup_experience"
|
||||
bulkSetAndroidAppsAvailableForHostTask SoftwareWorkerTask = "bulk_set_android_apps_available_for_host"
|
||||
makeAndroidAppsAvailableForHostTask SoftwareWorkerTask = "make_android_apps_available_for_host"
|
||||
makeAndroidAppAvailableTask SoftwareWorkerTask = "make_android_app_available"
|
||||
runAndroidSetupExperienceTask SoftwareWorkerTask = "run_android_setup_experience"
|
||||
bulkSetAndroidAppsAvailableForHostTask SoftwareWorkerTask = "bulk_set_android_apps_available_for_host"
|
||||
bulkSetAndroidAppsAvailableForHostsTask SoftwareWorkerTask = "bulk_set_android_apps_available_for_hosts"
|
||||
)
|
||||
|
||||
type softwareWorkerArgs struct {
|
||||
@@ -59,7 +60,8 @@ type softwareWorkerArgs struct {
|
||||
|
||||
// AppConfigChanged indicates if the android app configuration changed as part
|
||||
// of the action that triggered this task.
|
||||
AppConfigChanged bool `json:"app_config_changed,omitempty"`
|
||||
AppConfigChanged bool `json:"app_config_changed,omitempty"`
|
||||
UUIDsToIDs map[string]uint `json:"uuids_to_ids,omitempty"`
|
||||
}
|
||||
|
||||
func (v *SoftwareWorker) Run(ctx context.Context, argsJSON json.RawMessage) error {
|
||||
@@ -105,8 +107,16 @@ func (v *SoftwareWorker) Run(ctx context.Context, argsJSON json.RawMessage) erro
|
||||
), "running %s task",
|
||||
bulkSetAndroidAppsAvailableForHostTask)
|
||||
|
||||
case bulkSetAndroidAppsAvailableForHostsTask:
|
||||
return ctxerr.Wrapf(ctx, v.bulkSetAndroidAppsAvailableForHosts(
|
||||
ctx,
|
||||
args.UUIDsToIDs,
|
||||
args.EnterpriseName,
|
||||
), "running %s task", bulkSetAndroidAppsAvailableForHostsTask)
|
||||
|
||||
default:
|
||||
return ctxerr.Errorf(ctx, "unknown task: %v", args.Task)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,3 +466,61 @@ func QueueBulkSetAndroidAppsAvailableForHost(
|
||||
level.Debug(logger).Log("job_id", job.ID, "job_name", softwareWorkerJobName, "task", args.Task)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *SoftwareWorker) bulkSetAndroidAppsAvailableForHosts(ctx context.Context, uuidsToIDs map[string]uint, enterpriseName string) error {
|
||||
// for each host
|
||||
// get the set of self-service apps that are in scope for it
|
||||
for uuid, hostID := range uuidsToIDs {
|
||||
androidHost, err := v.Datastore.AndroidHostLiteByHostUUID(ctx, uuid)
|
||||
if err != nil {
|
||||
return ctxerr.Wrapf(ctx, err, "get android host by host UUID %s", uuid)
|
||||
}
|
||||
|
||||
appIDs, err := v.Datastore.GetAndroidAppsInScopeForHost(ctx, hostID)
|
||||
if err != nil {
|
||||
return ctxerr.WrapWithData(ctx, err, "get android apps in scope for host", map[string]any{"host_id": hostID})
|
||||
}
|
||||
|
||||
configsByAppID, err := v.Datastore.BulkGetAndroidAppConfigurations(ctx, appIDs, ptr.ValOrZero(androidHost.TeamID))
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "bulk get android app configurations")
|
||||
}
|
||||
|
||||
appPolicies, err := buildApplicationPolicyWithConfig(ctx, appIDs, configsByAppID, "AVAILABLE")
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "building application policies with config")
|
||||
}
|
||||
|
||||
err = v.AndroidModule.SetAppsForAndroidPolicy(ctx, enterpriseName, appPolicies, map[string]string{uuid: uuid})
|
||||
|
||||
if err != nil {
|
||||
return ctxerr.WrapWithData(ctx, err, "set apps for android policy", map[string]any{"host_id": hostID})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func QueueBulkSetAndroidAppsAvailableForHosts(
|
||||
ctx context.Context,
|
||||
ds fleet.Datastore,
|
||||
logger kitlog.Logger,
|
||||
uuidsToIDs map[string]uint,
|
||||
enterpriseName string) error {
|
||||
|
||||
args := &softwareWorkerArgs{
|
||||
Task: bulkSetAndroidAppsAvailableForHostsTask,
|
||||
UUIDsToIDs: uuidsToIDs,
|
||||
EnterpriseName: enterpriseName,
|
||||
}
|
||||
|
||||
job, err := QueueJob(ctx, ds, softwareWorkerJobName, args)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "queueing job")
|
||||
}
|
||||
|
||||
level.Debug(logger).Log("job_id", job.ID, "job_name", softwareWorkerJobName, "task", args.Task)
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user