Fix issue when identical MDM commands are sent twice to the same device when replica DB is being used. (#25355)

For #24816
Fix issue when identical MDM commands are sent twice to the same device
when replica DB is being used.

Root cause was that ctxdb.RequirePrimary wasn't used correctly, and
proper test was missing.

# Checklist for submitter

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
- [x] Added/updated automated tests
- [x] Manual QA for all new/changed functionality
This commit is contained in:
Victor Lyuboslavsky 2025-01-10 16:50:22 -06:00 committed by GitHub
parent d26afe3e62
commit 7aa3fee45b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 62 additions and 1 deletions

View file

@ -0,0 +1 @@
Fix issue when identical MDM commands are sent twice to the same device when replica DB is being used.

View file

@ -256,7 +256,7 @@ func (s *Service) CommandAndReportResults(r *mdm.Request, results *mdm.CommandRe
}
if results.Status != "Idle" {
// If the host is not idle, we use primary DB since we just wrote results of previous command.
ctxdb.RequirePrimary(r.Context, true)
r.Context = ctxdb.RequirePrimary(r.Context, true)
}
cmd, err := s.store.RetrieveNextCommand(r, results.Status == "NotNow")
if err != nil {

View file

@ -0,0 +1,58 @@
package nanomdm
import (
"context"
"testing"
"github.com/fleetdm/fleet/v4/server/contexts/ctxdb"
"github.com/fleetdm/fleet/v4/server/mdm/nanomdm/mdm"
mock "github.com/fleetdm/fleet/v4/server/mock/mdm"
"github.com/micromdm/nanolib/log"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCommandAndReportResultsPrimaryDBUse(t *testing.T) {
ds := new(mock.MDMAppleStore)
enrollID := &mdm.EnrollID{
ID: "1",
Type: mdm.Device,
}
s := Service{
logger: log.NopLogger,
store: ds,
normalizer: func(e *mdm.Enrollment) *mdm.EnrollID {
return enrollID
},
}
ds.StoreCommandReportFunc = func(r *mdm.Request, report *mdm.CommandResults) error {
return nil
}
var primaryRequired bool
ds.RetrieveNextCommandFunc = func(r *mdm.Request, skipNotNow bool) (*mdm.CommandWithSubtype, error) {
assert.Equal(t, primaryRequired, ctxdb.IsPrimaryRequired(r.Context))
return nil, nil
}
mdmRequest := &mdm.Request{
Context: context.Background(),
}
mdmCommandResults := &mdm.CommandResults{
Status: "Idle",
}
// We don't use primary DB with "Idle" status because we don't update the status of existing commands
cmd, err := s.CommandAndReportResults(mdmRequest, mdmCommandResults)
require.NoError(t, err)
require.Nil(t, cmd)
// We use primary DB with non-"Idle" status
mdmCommandResults = &mdm.CommandResults{
Status: "Acknowledge",
}
primaryRequired = true
cmd, err = s.CommandAndReportResults(mdmRequest, mdmCommandResults)
require.NoError(t, err)
require.Nil(t, cmd)
}

View file

@ -11583,6 +11583,8 @@ func (s *integrationMDMTestSuite) TestVPPApps() {
// Simulate successful installation on the host
cmd, err = mdmClient.Acknowledge(cmd.CommandUUID)
require.NoError(t, err)
// No further commands expected
assert.Nil(t, cmd)
listResp = listHostsResponse{}
s.DoJSON("GET", "/api/latest/fleet/hosts", nil, http.StatusOK, &listResp, "software_status", "installed", "team_id",