Reconcile MDM profiles by updating installed profiles in place#10998
Reconcile MDM profiles by updating installed profiles in place#10998gillespi314 wants to merge 10 commits intomainfrom
Conversation
mna
left a comment
There was a problem hiding this comment.
I think this is an awesome approach that could very well work (while being simpler than what I had in mind)! It assumes the queue is reliable and runs commands in sequence for a given host - if we can rely on that, great. AIUI, it uses the fact that profiles are unique per team and profile identifier to detect when a Remove+Install is needed vs a Replace, without having to do that logic when saving the profiles for the team/no team (i.e. we still do a delete+insert at that place, meaning that an update to the contents of the profile results in a new internal profile ID at the DB level).
The other points I mention in the review are, I think, all solvable. I think the way to identify if the operation should be a replace needs to be done per "host UUID+profile identifier" tuple, and not just profile identifier.
| // | ||
| // We'll do another pass at the end to revert any changes for failed | ||
| // delivieries. | ||
| if err := ds.BulkDeleteMDMAppleHostProfiles(ctx, replacedHostProfiles); err != nil { |
There was a problem hiding this comment.
I think this should be done only once the Install of the replaced profile is successfully done, otherwise we remove a profile from our table for a host, but it may still have the old one "to-be-replaced".
There was a problem hiding this comment.
My thinking here was that if the install fails, there would be an entry for the install failure, which would tie back to the profile identifier of the to-be-replaced profile. I think that is consistent with what the UI wants to display, but do you think we need more than that entry for our own table hygiene?
There was a problem hiding this comment.
Ah yeah, you're right, the profile to be removed would be deleted from the table, but the one to be inserted as replacement would also be inserted and with a status NULL so that enqueuing the Install command is retried on the next run. It would be reported in stats/UI/etc. as "pending install" which is exactly what we want.
| installTargets, removeTargets := make(map[uint]*cmdTarget), make(map[uint]*cmdTarget) | ||
| for _, p := range toInstall { | ||
| toGetContents[p.ProfileID] = true | ||
| toInstallIdentifiers[p.ProfileIdentifier] = true |
There was a problem hiding this comment.
This assumes that when a ProfileIdentifier is seen in toInstall, then it is necessarily being replaced for all hosts if it is also seen in toRemove. I'm not sure this is right, e.g.
- Add profile with Identifier
Custom1to team 1 - Add profile with Identifier
Custom1to team 2 - ReconcileProfiles deploys the profile to hosts in team 1 and 2
- Update profile
Custom1for team 1 (must be replaced for hosts in team 1) - Remove profile
Custom1for team 2 (must be deleted for hosts in team 2) - ReconcileProfiles would see that
Custom1needs to be installed (because it needs to, but just for hosts in team 1) - ReconcileProfiles sees that all hosts in teams 1 and 2 have
Custom1to be removed - Thus it does not send a
Removefor all hosts in teams 1 and 2, expecting them to have the profile replaced (which is true only for hosts in team 1) - Result would be (I think) that hosts in team 2 still have the profile installed, but it is deleted from our host profiles table
There was a problem hiding this comment.
Thanks! I've modified the map to use the combined key (HostUUID,ProfileIdentifier) and I'll add some tests to cover these scenarios.
| WHERE host_uuid IN (?) | ||
| AND ((operation_type = ? | ||
| AND profile_identifier IN (?)) | ||
| OR profile_id IN (?))` |
There was a problem hiding this comment.
I don't think the query is quite right - it will delete rows where the host uuid is in the list of uuids whenever it is part of the profile identifiers or ids, but maybe it shouldn't be deleted for some particular combinations.
E.g. if I call this with [{hostA, profID1}, {hostB, profID2}], it would delete the row if hostA happens to have profile profID2.
There was a problem hiding this comment.
Thanks again! I've modified query and I'll add some tests that cover this too.
Codecov ReportPatch coverage:
Additional details and impacted files@@ Coverage Diff @@
## main #10998 +/- ##
==========================================
- Coverage 60.41% 60.40% -0.02%
==========================================
Files 522 524 +2
Lines 54200 54392 +192
==========================================
+ Hits 32747 32855 +108
- Misses 18468 18542 +74
- Partials 2985 2995 +10
... and 13 files with indirect coverage changes Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report in Codecov by Sentry. |
### Related tickets #10775 #10678 #11024 #11026 ### What's happening - Implemented the hashing mechanism defined by @mna in #10678, however this mechanism is mainly relevant for batch profile updates via the CLI, we can't leverage it when a host switches teams. - Modified `BulkSetPendingMDMAppleHostProfiles` so when two profiles with the same identifier are sheduled both for removal and update, the function will now mark only the `install` as `pending` so it's picked by the cron, and will `DELETE` the `remove` entry from the database so it's not picked by the cron and never sent to the user. - `GetHostMDMProfiles` and consequently the profiles returned in `GET /api/_version_/fleet/hosts` return `host_mdm_apple_profiles.state = NULL` as "Enforcing (pending", the distinction between `status = 'pending'` and `status IS NULL` is only useful for the cron, for users both mean the same thing, and all our profile aggregations already behave this way. - Using the solution implemented by @gillespi314 in #10998 we're now deleting the host row from `host_disk_encryption_keys` if a host is moved from a team that enforces disk encryption to a team that doesn't. # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Added/updated tests - [x] Manual QA for all new/changed functionality
|
Closed in favor of #11084 |
### Related tickets fleetdm#10775 fleetdm#10678 fleetdm#11024 fleetdm#11026 ### What's happening - Implemented the hashing mechanism defined by @mna in fleetdm#10678, however this mechanism is mainly relevant for batch profile updates via the CLI, we can't leverage it when a host switches teams. - Modified `BulkSetPendingMDMAppleHostProfiles` so when two profiles with the same identifier are sheduled both for removal and update, the function will now mark only the `install` as `pending` so it's picked by the cron, and will `DELETE` the `remove` entry from the database so it's not picked by the cron and never sent to the user. - `GetHostMDMProfiles` and consequently the profiles returned in `GET /api/_version_/fleet/hosts` return `host_mdm_apple_profiles.state = NULL` as "Enforcing (pending", the distinction between `status = 'pending'` and `status IS NULL` is only useful for the cron, for users both mean the same thing, and all our profile aggregations already behave this way. - Using the solution implemented by @gillespi314 in fleetdm#10998 we're now deleting the host row from `host_disk_encryption_keys` if a host is moved from a team that enforces disk encryption to a team that doesn't. # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Added/updated tests - [x] Manual QA for all new/changed functionality
Issues #10775 & #10678
Revise
ReconcileProfilescron job so that whenever a profile to be installed shares the same profile identifier as an already installed profile, the profile is updated in place (i.e. do not send aRemoveProfilecommand first).Checklist for submitter
If some of the following don't apply, delete the relevant line.
changes/ororbit/changes/.See Changes files for more information.
SELECT *is avoided, SQL injection is prevented (using placeholders for values in statements)cmd/osquery-perffor new osquery data ingestion features.