mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
Fixed Android pubsub panic when host was deleted
This commit is contained in:
parent
ca1ab21cbc
commit
137ec3f406
3 changed files with 75 additions and 0 deletions
1
changes/42494-android-pubsub-panic-deleted-host
Normal file
1
changes/42494-android-pubsub-panic-deleted-host
Normal file
|
|
@ -0,0 +1 @@
|
|||
- Fixed a server panic (502) when an Android pubsub status report arrived for a host that had been deleted from Fleet.
|
||||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/server/contexts/ctxdb"
|
||||
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
"github.com/fleetdm/fleet/v4/server/mdm/android"
|
||||
|
|
@ -210,6 +211,16 @@ func (svc *Service) handlePubSubStatusReport(ctx context.Context, token string,
|
|||
svc.logger.DebugContext(ctx, "Error re-enrolling Android host", "data", rawData)
|
||||
return ctxerr.Wrap(ctx, err, "re-enrolling deleted Android host")
|
||||
}
|
||||
// Re-fetch the host so the subsequent updateHost/updateHostSoftware calls have a
|
||||
// non-nil host. Force primary: enrollHost just INSERTed the host on the writer,
|
||||
// and the default reader can be on a replica that hasn't caught up yet.
|
||||
host, err = svc.getExistingHost(ctxdb.RequirePrimary(ctx, true), &device)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "getting re-enrolled Android host")
|
||||
}
|
||||
if host == nil {
|
||||
return ctxerr.New(ctx, "re-enrolled Android host not found")
|
||||
}
|
||||
}
|
||||
err = svc.updateHost(ctx, &device, host, false)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/server/config"
|
||||
"github.com/fleetdm/fleet/v4/server/contexts/ctxdb"
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
"github.com/fleetdm/fleet/v4/server/mdm/android"
|
||||
android_mock "github.com/fleetdm/fleet/v4/server/mdm/android/mock"
|
||||
|
|
@ -1699,3 +1700,65 @@ func TestStatusReportAppInstallVerification(t *testing.T) {
|
|||
require.True(t, mockDS.GetPastActivityDataForAndroidVPPAppInstallFuncInvoked)
|
||||
})
|
||||
}
|
||||
|
||||
// Status report arrives for an Android host that was deleted from Fleet: the
|
||||
// handler must re-enroll the host and then read it back from primary (issue #42494).
|
||||
func TestPubSubStatusReportHostDeletedFromFleet(t *testing.T) {
|
||||
svc, mockDS := createAndroidService(t)
|
||||
|
||||
mockDS.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
|
||||
return &fleet.AppConfig{MDM: fleet.MDM{AndroidEnabledAndConfigured: true}}, nil
|
||||
}
|
||||
|
||||
// AndroidHostLite returns not-found initially (host was deleted from Fleet) and
|
||||
// returns the created host only after enrollHost has run.
|
||||
var createdHost *fleet.AndroidHost
|
||||
mockDS.AndroidHostLiteFunc = func(ctx context.Context, enterpriseSpecificID string) (*fleet.AndroidHost, error) {
|
||||
if createdHost != nil && ctxdb.IsPrimaryRequired(ctx) {
|
||||
return createdHost, nil
|
||||
}
|
||||
return nil, common_mysql.NotFound("android host lite mock")
|
||||
}
|
||||
mockDS.VerifyEnrollSecretFunc = func(ctx context.Context, secret string) (*fleet.EnrollSecret, error) {
|
||||
return &fleet.EnrollSecret{Secret: "global"}, nil
|
||||
}
|
||||
mockDS.NewAndroidHostFunc = func(ctx context.Context, host *fleet.AndroidHost, companyOwned bool) (*fleet.AndroidHost, error) {
|
||||
createdHost = host
|
||||
return host, nil
|
||||
}
|
||||
mockDS.UpdateAndroidHostFunc = func(ctx context.Context, host *fleet.AndroidHost, fromEnroll, companyOwned bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Minimal device: no AppliedPolicyName / ApplicationReports, so the status report
|
||||
// handler stays on the simple path (skips policy + software verification).
|
||||
// EnrollmentTokenData is present because production AMAPI payloads include it;
|
||||
// without it, enrollHost would fail to unmarshal and never exercise the fix.
|
||||
device := androidmanagement.Device{
|
||||
Name: createAndroidDeviceId("deleted-from-fleet"),
|
||||
EnrollmentTokenData: `{"enroll_secret": "global"}`,
|
||||
HardwareInfo: &androidmanagement.HardwareInfo{
|
||||
EnterpriseSpecificId: strings.ToUpper(uuid.New().String()),
|
||||
Brand: "TestBrand",
|
||||
Model: "TestModel",
|
||||
SerialNumber: "test-serial",
|
||||
Hardware: "test-hardware",
|
||||
},
|
||||
SoftwareInfo: &androidmanagement.SoftwareInfo{AndroidBuildNumber: "test-build", AndroidVersion: "1"},
|
||||
MemoryInfo: &androidmanagement.MemoryInfo{
|
||||
TotalRam: int64(8 * 1024 * 1024 * 1024),
|
||||
TotalInternalStorage: int64(64 * 1024 * 1024 * 1024),
|
||||
},
|
||||
}
|
||||
data, err := json.Marshal(device)
|
||||
require.NoError(t, err)
|
||||
statusReport := &android.PubSubMessage{
|
||||
Attributes: map[string]string{"notificationType": string(android.PubSubStatusReport)},
|
||||
Data: base64.StdEncoding.EncodeToString(data),
|
||||
}
|
||||
|
||||
err = svc.ProcessPubSubPush(context.Background(), "value", statusReport)
|
||||
require.NoError(t, err)
|
||||
require.True(t, mockDS.NewAndroidHostFuncInvoked, "re-enrollment should create the host")
|
||||
require.True(t, mockDS.UpdateAndroidHostFuncInvoked, "status report update should run against the re-enrolled host")
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue