Skip to content

make DDM name check case insensitive#43347

Open
MagnusHJensen wants to merge 4 commits intomainfrom
40332-make-ddm-name-check-case-insensitive
Open

make DDM name check case insensitive#43347
MagnusHJensen wants to merge 4 commits intomainfrom
40332-make-ddm-name-check-case-insensitive

Conversation

@MagnusHJensen
Copy link
Copy Markdown
Member

@MagnusHJensen MagnusHJensen commented Apr 9, 2026

Related issue: Resolves #40322

The issue was due to case insensitivity in go maps, so when we checked if we should keep the DDM profile or not, it failed. So we now default to lowercasing all profile names to make it case insensitive.

While that fixes the cause, for the issue, I will follow up with another PR for the all profiles stuck in pending, since it's a scaling issue due to batching and always taking installs before removes, so with 11k hosts it would never have both install and remove in the same run, failing to clear out the stuck pending. It can be manually remediated, but we want to have a better fix for this that actually cleans it up, if this is met as it can be perfectly valid.

Checklist for submitter

If some of the following don't apply, delete the relevant line.

  • Changes file added for user-visible changes in changes/, orbit/changes/ or ee/fleetd-chrome/changes.
    See Changes files for more information.

  • Input data is properly validated, SELECT * is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters.

  • Timeouts are implemented and retries are limited to avoid infinite loops

  • If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes

Testing

  • Added/updated automated tests
  • QA'd all new/changed functionality manually

Summary by CodeRabbit

  • Bug Fixes

    • Fixed Apple MDM profile batch operations to handle declaration names case-insensitively. Updating a declaration with different name casing (e.g., mixed-case to lowercase) no longer creates duplicate profiles or triggers unnecessary operations.
  • Tests

    • Added test coverage for case-insensitive Apple MDM declaration name handling across team and non-team configurations.

@MagnusHJensen MagnusHJensen requested a review from a team as a code owner April 9, 2026 18:16
Copilot AI review requested due to automatic review settings April 9, 2026 18:16
Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to prevent Apple DDM declarations/profiles from being recreated (and resent) when only the profile display name casing changes by making the “name match” logic case-insensitive.

Changes:

  • Normalize incoming/existing declaration names to lowercase for in-memory matching during batch set.
  • Preserve existing declaration_uuid across case-only name changes and update host declaration name records after updates.
  • Add an automated regression test covering case-only name changes and ensuring host install states are not disrupted.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 4 comments.

File Description
server/datastore/mysql/apple_mdm.go Lowercases names for matching; preserves declaration UUID on case-only changes; updates host_mdm_apple_declarations.declaration_name after declaration updates.
server/datastore/mysql/apple_mdm_test.go Adds a regression test verifying case-only changes don’t create duplicates or disrupt install/pending states.
changes/40332-make-ddm-name-check-case-insensitive Adds a changelog entry describing the user-visible fix.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 9, 2026

Codecov Report

❌ Patch coverage is 80.00000% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.88%. Comparing base (9ba1e7e) to head (2836ba3).
⚠️ Report is 22 commits behind head on main.

Files with missing lines Patch % Lines
server/datastore/mysql/apple_mdm.go 80.00% 3 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #43347      +/-   ##
==========================================
+ Coverage   66.86%   66.88%   +0.01%     
==========================================
  Files        2586     2589       +3     
  Lines      207474   207660     +186     
  Branches     9276     9276              
==========================================
+ Hits       138736   138898     +162     
- Misses      56109    56121      +12     
- Partials    12629    12641      +12     
Flag Coverage Δ
backend 68.67% <80.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@MagnusHJensen
Copy link
Copy Markdown
Member Author

Failing test is unrelated

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

Walkthrough

The changes address an issue where updating a DDM configuration profile's display name with different casing triggered profile recreation and stuck remove operations. The fix implements case-insensitive name matching for DDM declarations while preserving the updated casing in the profile name. Changes include normalization of incoming declaration names to lowercase for lookups, synchronization of declaration names across data structures, and tests verifying that case-only name changes do not create duplicate declarations or alter host profile operation statuses.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'make DDM name check case insensitive' directly and clearly summarizes the main change in the changeset.
Description check ✅ Passed The description includes a linked issue reference, explains the root cause, describes the fix approach, and has the submitter checklist properly completed with relevant items checked.
Linked Issues check ✅ Passed The code changes directly address issue #40322 by making DDM profile name checks case-insensitive through lowercasing, preventing stuck remove operations when only display name casing changes.
Out of Scope Changes check ✅ Passed All changes in the PR are directly related to the objective of making DDM name checks case-insensitive; no out-of-scope modifications are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 40332-make-ddm-name-check-case-insensitive

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@server/datastore/mysql/apple_mdm.go`:
- Around line 5111-5113: When building incomingDeclarationsMap using normalized
keys (strings.ToLower(p.Name)), detect if a normalized name already exists and
reject the request instead of overwriting: inside the loop where
incomingNames[i] and incomingDeclarationsMap[name] are assigned, check if
incomingDeclarationsMap already has an entry for that normalized name and return
an error (including both conflicting original names/p.Name values) so duplicate
declarations like "Foo" and "foo" are rejected; update the function that
contains incomingNames/incomingDeclarationsMap to perform this validation before
continuing with upserts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f9cec3b2-a5c4-4c10-9b55-59a43ac76e73

📥 Commits

Reviewing files that changed from the base of the PR and between 0bf48d3 and 2836ba3.

📒 Files selected for processing (3)
  • changes/40322-make-ddm-name-check-case-insensitive
  • server/datastore/mysql/apple_mdm.go
  • server/datastore/mysql/apple_mdm_test.go

Comment on lines +5111 to +5113
name := strings.ToLower(p.Name)
incomingNames[i] = name
incomingDeclarationsMap[name] = p
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Reject duplicate declaration names after normalization.

Lowercasing the key here makes two incoming declarations like Foo and foo collapse into one incomingDeclarationsMap entry. The rest of the flow still iterates both original declarations, so one upsert can overwrite the other while only one declaration's labels/UUID bookkeeping survives. Please validate and return an error when two incoming names normalize to the same value instead of letting the last one win.

Proposed fix
 	incomingNames := make([]string, len(incomingDeclarations))
 	incomingDeclarationsMap := make(map[string]*fleet.MDMAppleDeclaration, len(incomingDeclarations))
 	for i, p := range incomingDeclarations {
-		name := strings.ToLower(p.Name)
-		incomingNames[i] = name
-		incomingDeclarationsMap[name] = p
+		name := strings.ToLower(p.Name)
+		if existing, ok := incomingDeclarationsMap[name]; ok {
+			return false, &fleet.BadRequestError{
+				Message: fmt.Sprintf(
+					"declaration names %q and %q differ only by letter case; declaration names must be unique",
+					existing.Name, p.Name,
+				),
+			}
+		}
+		incomingNames[i] = name
+		incomingDeclarationsMap[name] = p
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
name := strings.ToLower(p.Name)
incomingNames[i] = name
incomingDeclarationsMap[name] = p
name := strings.ToLower(p.Name)
if existing, ok := incomingDeclarationsMap[name]; ok {
return false, &fleet.BadRequestError{
Message: fmt.Sprintf(
"declaration names %q and %q differ only by letter case; declaration names must be unique",
existing.Name, p.Name,
),
}
}
incomingNames[i] = name
incomingDeclarationsMap[name] = p
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/datastore/mysql/apple_mdm.go` around lines 5111 - 5113, When building
incomingDeclarationsMap using normalized keys (strings.ToLower(p.Name)), detect
if a normalized name already exists and reject the request instead of
overwriting: inside the loop where incomingNames[i] and
incomingDeclarationsMap[name] are assigned, check if incomingDeclarationsMap
already has an entry for that normalized name and return an error (including
both conflicting original names/p.Name values) so duplicate declarations like
"Foo" and "foo" are rejected; update the function that contains
incomingNames/incomingDeclarationsMap to perform this validation before
continuing with upserts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Changing the name of a DDM configuration profile caused a stuck remove operation

2 participants