fleet/server/worker/software_worker_test.go

168 lines
6.3 KiB
Go
Raw Normal View History

package worker
import (
"context"
"encoding/json"
"testing"
"github.com/fleetdm/fleet/v4/server/datastore/mysql"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/mdm/android"
"github.com/fleetdm/fleet/v4/server/mock"
"github.com/fleetdm/fleet/v4/server/ptr"
kitlog "github.com/go-kit/log"
"github.com/stretchr/testify/require"
"google.golang.org/api/androidmanagement/v1"
)
func TestSoftwareWorker(t *testing.T) {
ds := mysql.CreateMySQLDS(t)
// call TruncateTables immediately as some DB migrations may create jobs
mysql.TruncateTables(t, ds)
mysql.SetTestABMAssets(t, ds, "fleet")
}
// mockAndroidModule is a mock implementation of the android.Service interface for testing.
type mockAndroidModule struct {
android.Service
buildFleetAgentApplicationPolicyFunc func(ctx context.Context, hostUUID string) (*androidmanagement.ApplicationPolicy, error)
setAppsForAndroidPolicyFunc func(ctx context.Context, enterpriseName string, appPolicies []*androidmanagement.ApplicationPolicy, hostUUIDs map[string]string) error
}
func (m *mockAndroidModule) BuildFleetAgentApplicationPolicy(ctx context.Context, hostUUID string) (*androidmanagement.ApplicationPolicy, error) {
if m.buildFleetAgentApplicationPolicyFunc != nil {
return m.buildFleetAgentApplicationPolicyFunc(ctx, hostUUID)
}
return nil, nil
}
func (m *mockAndroidModule) SetAppsForAndroidPolicy(ctx context.Context, enterpriseName string, appPolicies []*androidmanagement.ApplicationPolicy, hostUUIDs map[string]string) error {
if m.setAppsForAndroidPolicyFunc != nil {
return m.setAppsForAndroidPolicyFunc(ctx, enterpriseName, appPolicies, hostUUIDs)
}
return nil
}
// TestBulkSetAndroidAppsAvailableForHostsPreservesFleetAgent verifies that the Fleet Agent
// is preserved when an Android host is transferred between teams. This prevents the agent
// from being uninstalled (and losing state) during team transfers.
func TestBulkSetAndroidAppsAvailableForHostsPreservesFleetAgent(t *testing.T) {
ctx := t.Context()
hostUUID := "test-host-uuid"
hostID := uint(1)
teamID := uint(2)
ds := new(mock.Store)
ds.AndroidHostLiteByHostUUIDFunc = func(ctx context.Context, uuid string) (*fleet.AndroidHost, error) {
return &fleet.AndroidHost{
Host: &fleet.Host{
ID: hostID,
UUID: hostUUID,
TeamID: ptr.Uint(teamID),
},
}, nil
}
ds.SetHostCertificateTemplatesToPendingRemoveForHostFunc = func(ctx context.Context, hostUUID string) error {
return nil
}
ds.CreatePendingCertificateTemplatesForNewHostFunc = func(ctx context.Context, hostUUID string, teamID uint) (int64, error) {
return 0, nil
}
ds.GetAndroidAppsInScopeForHostFunc = func(ctx context.Context, hostID uint) ([]string, error) {
return []string{"com.example.teamapp"}, nil
}
ds.BulkGetAndroidAppConfigurationsFunc = func(ctx context.Context, appIDs []string, globalOrTeamID uint) (map[string]json.RawMessage, error) {
return map[string]json.RawMessage{}, nil
}
var capturedAppPolicies []*androidmanagement.ApplicationPolicy
androidModule := &mockAndroidModule{
buildFleetAgentApplicationPolicyFunc: func(ctx context.Context, hostUUID string) (*androidmanagement.ApplicationPolicy, error) {
return &androidmanagement.ApplicationPolicy{
PackageName: "com.fleetdm.agent",
InstallType: "FORCE_INSTALLED",
}, nil
},
setAppsForAndroidPolicyFunc: func(ctx context.Context, enterpriseName string, appPolicies []*androidmanagement.ApplicationPolicy, hostUUIDs map[string]string) error {
capturedAppPolicies = appPolicies
return nil
},
}
worker := &SoftwareWorker{
Datastore: ds,
AndroidModule: androidModule,
Log: kitlog.NewNopLogger(),
}
err := worker.bulkSetAndroidAppsAvailableForHosts(ctx, map[string]uint{hostUUID: hostID}, "enterprises/test")
require.NoError(t, err)
// Verify both the team app and Fleet Agent are in the policy
require.Len(t, capturedAppPolicies, 2, "expected team app + Fleet Agent")
capturedPackageNames := make([]string, len(capturedAppPolicies))
for i, policy := range capturedAppPolicies {
capturedPackageNames[i] = policy.PackageName
}
require.ElementsMatch(t, []string{"com.example.teamapp", "com.fleetdm.agent"}, capturedPackageNames)
}
Point to com.fleetdm.agent Android agent by default. (#37770) <!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #37736 Enabling the public Android agent for Android MDM, by default - `com.fleetdm.agent` Also bug fix: [Preserve Fleet Agent in Android policy during GitOps/API app updates.](https://github.com/fleetdm/fleet/pull/37770/commits/9b3ccf55dc6f09078271d1f6a2e411e2220ee81d) # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually For unreleased bug fixes in a release candidate, one of: - [x] Confirmed that the fix is not expected to adversely impact load test results <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * Added Android agent application with automatic deployment via Android MDM to support SCEP certificate management on Android devices. * Introduced configurable Android agent settings for package name and signing certificate. * **Documentation** * Updated Android MDM configuration documentation with environment variable and YAML configuration examples for Android agent deployment. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-07 00:11:23 +00:00
// TestBulkMakeAndroidAppsAvailableForHostPreservesFleetAgent verifies that the Fleet Agent
// is preserved when BatchAssociateVPPApps updates Android apps for a host.
// This is the singular version called from BatchAssociateVPPApps.
func TestBulkMakeAndroidAppsAvailableForHostPreservesFleetAgent(t *testing.T) {
ctx := t.Context()
hostUUID := "test-host-uuid"
policyID := "test-policy-id"
teamID := uint(2)
ds := new(mock.Store)
ds.AndroidHostLiteByHostUUIDFunc = func(ctx context.Context, uuid string) (*fleet.AndroidHost, error) {
return &fleet.AndroidHost{
Host: &fleet.Host{
UUID: hostUUID,
TeamID: ptr.Uint(teamID),
},
}, nil
}
ds.BulkGetAndroidAppConfigurationsFunc = func(ctx context.Context, appIDs []string, globalOrTeamID uint) (map[string]json.RawMessage, error) {
return map[string]json.RawMessage{}, nil
}
var capturedAppPolicies []*androidmanagement.ApplicationPolicy
androidModule := &mockAndroidModule{
buildFleetAgentApplicationPolicyFunc: func(ctx context.Context, hostUUID string) (*androidmanagement.ApplicationPolicy, error) {
return &androidmanagement.ApplicationPolicy{
PackageName: "com.fleetdm.agent",
InstallType: "FORCE_INSTALLED",
}, nil
},
setAppsForAndroidPolicyFunc: func(ctx context.Context, enterpriseName string, appPolicies []*androidmanagement.ApplicationPolicy, hostUUIDs map[string]string) error {
capturedAppPolicies = appPolicies
return nil
},
}
worker := &SoftwareWorker{
Datastore: ds,
AndroidModule: androidModule,
Log: kitlog.NewNopLogger(),
}
// Simulate adding a VPP app via BatchAssociateVPPApps
applicationIDs := []string{"com.example.vppapp"}
err := worker.bulkMakeAndroidAppsAvailableForHost(ctx, hostUUID, policyID, applicationIDs, "enterprises/test")
require.NoError(t, err)
// Verify both the VPP app and Fleet Agent are in the policy
require.Len(t, capturedAppPolicies, 2, "expected VPP app + Fleet Agent")
capturedPackageNames := make([]string, len(capturedAppPolicies))
for i, policy := range capturedAppPolicies {
capturedPackageNames[i] = policy.PackageName
}
require.ElementsMatch(t, []string{"com.example.vppapp", "com.fleetdm.agent"}, capturedPackageNames)
}