Skip to content

Commit 7aa3fee

Browse files
authored
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
1 parent d26afe3 commit 7aa3fee

File tree

4 files changed

+62
-1
lines changed

4 files changed

+62
-1
lines changed

changes/24816-fix-double-mdm-commands

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix issue when identical MDM commands are sent twice to the same device when replica DB is being used.

server/mdm/nanomdm/service/nanomdm/service.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ func (s *Service) CommandAndReportResults(r *mdm.Request, results *mdm.CommandRe
256256
}
257257
if results.Status != "Idle" {
258258
// If the host is not idle, we use primary DB since we just wrote results of previous command.
259-
ctxdb.RequirePrimary(r.Context, true)
259+
r.Context = ctxdb.RequirePrimary(r.Context, true)
260260
}
261261
cmd, err := s.store.RetrieveNextCommand(r, results.Status == "NotNow")
262262
if err != nil {
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package nanomdm
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/fleetdm/fleet/v4/server/contexts/ctxdb"
8+
"github.com/fleetdm/fleet/v4/server/mdm/nanomdm/mdm"
9+
mock "github.com/fleetdm/fleet/v4/server/mock/mdm"
10+
"github.com/micromdm/nanolib/log"
11+
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestCommandAndReportResultsPrimaryDBUse(t *testing.T) {
16+
ds := new(mock.MDMAppleStore)
17+
18+
enrollID := &mdm.EnrollID{
19+
ID: "1",
20+
Type: mdm.Device,
21+
}
22+
s := Service{
23+
logger: log.NopLogger,
24+
store: ds,
25+
normalizer: func(e *mdm.Enrollment) *mdm.EnrollID {
26+
return enrollID
27+
},
28+
}
29+
ds.StoreCommandReportFunc = func(r *mdm.Request, report *mdm.CommandResults) error {
30+
return nil
31+
}
32+
var primaryRequired bool
33+
ds.RetrieveNextCommandFunc = func(r *mdm.Request, skipNotNow bool) (*mdm.CommandWithSubtype, error) {
34+
assert.Equal(t, primaryRequired, ctxdb.IsPrimaryRequired(r.Context))
35+
return nil, nil
36+
}
37+
38+
mdmRequest := &mdm.Request{
39+
Context: context.Background(),
40+
}
41+
mdmCommandResults := &mdm.CommandResults{
42+
Status: "Idle",
43+
}
44+
// We don't use primary DB with "Idle" status because we don't update the status of existing commands
45+
cmd, err := s.CommandAndReportResults(mdmRequest, mdmCommandResults)
46+
require.NoError(t, err)
47+
require.Nil(t, cmd)
48+
49+
// We use primary DB with non-"Idle" status
50+
mdmCommandResults = &mdm.CommandResults{
51+
Status: "Acknowledge",
52+
}
53+
primaryRequired = true
54+
cmd, err = s.CommandAndReportResults(mdmRequest, mdmCommandResults)
55+
require.NoError(t, err)
56+
require.Nil(t, cmd)
57+
58+
}

server/service/integration_mdm_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11583,6 +11583,8 @@ func (s *integrationMDMTestSuite) TestVPPApps() {
1158311583
// Simulate successful installation on the host
1158411584
cmd, err = mdmClient.Acknowledge(cmd.CommandUUID)
1158511585
require.NoError(t, err)
11586+
// No further commands expected
11587+
assert.Nil(t, cmd)
1158611588

1158711589
listResp = listHostsResponse{}
1158811590
s.DoJSON("GET", "/api/latest/fleet/hosts", nil, http.StatusOK, &listResp, "software_status", "installed", "team_id",

0 commit comments

Comments
 (0)