fix(saltclass): deepcopy merged list and value overrides in dict_merge - #70031
fix(saltclass): deepcopy merged list and value overrides in dict_merge#70031waterWang wants to merge 5 commits into
Conversation
In expanded_dict_from_minion(), the code was merging the entire exp_dict (which includes 'pillars', 'states', 'classes' keys) into pillars_dict. This caused the pillars dict to be nested under a 'pillars' key and polluted with non-pillar keys. Fix: merge only exp_dict["pillars"] into pillars_dict, and read pillars_dict directly in get_pillars() instead of navigating through the extra 'pillars' wrapper key. Closes saltstack#70022
|
Hi there! Welcome to the Salt Community! Thank you for making your first contribution. We have a lengthy process for issues and PRs. Someone from the Core Team will follow up as soon as possible. In the meantime, here's some information that may help as you continue your Salt journey. There are lots of ways to get involved in our community. Every month, there are around a dozen opportunities to meet with other contributors and the Salt Core team and collaborate in real time. The best way to keep track is by subscribing to the Salt Community Events Calendar. |
Use copy.deepcopy when assigning values from dict b to dict a in dict_merge, to prevent shared reference mutation side effects. When dict_merge is called with an empty accumulator dict (a) and a source dict (b), the assignment a[key] = b[key] creates a shared reference. A subsequent call to dict_merge that extends a[key] (when both are lists) will mutate the original b[key] as well, corrupting the source data. This was causing SaltClass pillar merging to produce duplicate entries: the first class's pillar list was silently extended with the second class's data, then both the polluted first class and the correct second class contributed to the merged result. Fixes saltstack#70022
|
the AI is strong with this one |
What does this PR do?
Fixes duplicate pillar entries in SaltClass pillar merging (issue #70022).
Root cause
When
dict_mergeis called with an empty accumulator dicta = {}and a source dictb = {"data": [...]}:The
a[key]becomes a shared reference to the originalb[key]list. When a subsequentdict_mergecall encounters the same key and both values are lists, it callsa[key].extend(b[key])— which mutates the originalb[key]through the shared reference.In the SaltClass flow:
test1's pillars are merged into__pillar__(empty).__pillar__["data"]becomes a shared reference totest1's pillar list.test2's pillars are merged into__pillar__.__pillar__["data"].extend(test2_data)extends the shared list, polluting test1's original pillar data.Fix
Replace
a[key] = b[key]witha[key] = copy.deepcopy(b[key])indict_mergeto prevent shared reference mutation side effects. This is a single-line change that fixes the root cause.What issues does this PR fix or reference?
Fixes #70022