fix: preserve targetRevision in multi-source apps #26405
+151
−6
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fixed a bug where child Applications in App-of-Apps pattern would stop syncing correctly when RespectIgnoreDifferences sync option is enabled together with global ignoreDifferences configuration targeting fields within arrays.
Problem
When using the App-of-Apps pattern with the following configuration:
The targetRevision field in child Application's spec.sources[] would not update during sync. The diff would correctly show the desired targetRevision change, but after sync the value would remain unchanged, effectively preventing version updates in multi-source Applications.
Root Cause
The normalizeTargetResources function in controller/sync.go uses JSON Merge Patch (RFC 7386) to apply normalization from live state to target resources for CRDs. JSON Merge Patch has a fundamental limitation: it replaces arrays entirely rather than merging individual elements.
When ignoreDifferences targets a field within an array element (like .spec.sources[].path), the normalization process would:
Solution
Changed the patching strategy for CRDs from JSON Merge Patch (RFC 7386) to JSON Patch (RFC 6902):
{"spec": {"sources": [entire array from live]}}- replaces whole array[{"op": "replace", "path": "/spec/sources/1/path", "value": ""}]- granular operations on specific paths