Skip to content

fix: respect ApplicationSet update policy in progressive sync (#29142) - #29145

Open
himeshp wants to merge 1 commit into
argoproj:masterfrom
himeshp:agent/fix-progressive-sync-no-update-policy
Open

fix: respect ApplicationSet update policy in progressive sync (#29142)#29145
himeshp wants to merge 1 commit into
argoproj:masterfrom
himeshp:agent/fix-progressive-sync-no-update-policy

Conversation

@himeshp

@himeshp himeshp commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Progressive Sync treated generated spec changes as pending updates even when the effective ApplicationSet policy did not allow updates. This caused the status to loop between Waiting and Pending.

This passes the effective policy into the progressive sync status calculation and only checks spec changes when updates are allowed.

Fixes #29142

We reproduced the issue with a RollingSync ApplicationSet using create-only. With the generated spec at v2 and the live Application at v1, the unpatched controller repeatedly moved the status between Waiting and Pending. With this fix, the status remained stable and the live Application was not updated.

Checklist:

  • Either (a) I've created an enhancement proposal and discussed it with the community, (b) this is a bug fix, or (c) this does not need to be in the release notes.
  • The title of the PR states what changed and the related issues number (used for the release note).
  • The title of the PR conforms to the Title of the PR
  • I've included "Closes [ISSUE #]" or "Fixes [ISSUE #]" in the description to automatically close the associated issue.
  • I've updated both the CLI and UI to expose my feature, or I plan to submit a second PR with them. Not applicable; this is a controller bug fix.
  • Does this PR require documentation updates? No; this restores the existing policy behavior.
  • I've updated documentation as required by this PR. No documentation changes are required.
  • I have signed off all my commits as required by DCO
  • I have written unit and/or e2e tests for my change.
  • My build is green (troubleshooting builds).
  • My new feature complies with the feature status guidelines. Not applicable; this is a bug fix.
  • I have added a brief description of why this PR is necessary and/or what this PR solves.
  • Optional. My organization is added to USERS.md. Not applicable.
  • Optional. For bug fixes, I've indicated what older releases this fix should be cherry-picked into (this may or may not happen depending on risk/complexity). Suggested: release-3.5, release-3.4, and release-3.3.

Signed-off-by: himeshp <himeshp@users.noreply.github.com>
@bunnyshell

bunnyshell Bot commented Aug 11, 2026

Copy link
Copy Markdown

✅ Preview Environment deployed on Bunnyshell

Component Endpoints
argocd https://argocd-6skye2.bunnyenv.com/
argocd-ttyd https://argocd-web-cli-6skye2.bunnyenv.com/

See: Environment Details | Pipeline Logs

Available commands (reply to this comment):

  • 🔴 /bns:stop to stop the environment
  • 🚀 /bns:deploy to redeploy the environment
  • /bns:delete to remove the environment

@himeshp
himeshp marked this pull request as ready for review August 11, 2026 12:45
@himeshp
himeshp requested a review from a team as a code owner August 11, 2026 12:46
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Fix progressive sync to honor effective ApplicationSet update policy

🐞 Bug fix 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Compute and reuse the effective ApplicationSet sync policy during reconciliation.
• Prevent progressive sync from flagging spec drift when updates are disallowed.
• Add regression tests to avoid Waiting/Pending status loops under create-only policies.
Diagram

graph TD
  A["ApplicationSet Controller"] --> B["Effective Policy"] --> C["ProgressiveSync Manager"] --> D["Status Calculator"] --> E["ApplicationSet Status"]
  D --> F["Spec Drift Check"]
  B --> F

  subgraph Legend
    direction LR
    _ctrl[Controller] ~~~ _policy[Policy Input] ~~~ _logic[Decision/Logic]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Pass booleans instead of policy object
  • ➕ Narrower API surface (e.g., allowUpdate only) for status calculation
  • ➕ Reduces coupling to policy type evolution
  • ➖ May need more flags later (create/delete/update), leading to parameter creep
  • ➖ Less self-describing than passing the resolved policy
2. Resolve effective policy inside ProgressiveSync Manager
  • ➕ Fewer parameters on PerformProgressiveSyncs/UpdateApplicationSetApplicationStatus
  • ➕ Centralizes policy resolution for all progressive sync logic
  • ➖ Manager currently lacks controller-level inputs (global policy + override enablement)
  • ➖ Risks duplicated resolution logic or hidden dependencies

Recommendation: Keep the current approach: compute effective policy once in the controller and pass it into progressive sync. This avoids duplicating policy-resolution logic, ensures consistent behavior across reconcile paths (create/update/delete and progressive sync), and fixes the underlying issue by aligning status transitions with what the write path is permitted to do.

Files changed (4) +76 / -13

Bug fix (2) +10 / -9
applicationset_controller.goCompute effective policy once and thread into reconcile paths +5/-4

Compute effective policy once and thread into reconcile paths

• Computes the effective ApplicationSet sync policy once per reconcile and reuses it for delete/update checks. Passes the effective policy into progressive sync execution to ensure status logic matches allowed operations.

applicationset/controllers/applicationset_controller.go

progressive_sync.goGate spec-drift status on AllowUpdate effective policy +5/-5

Gate spec-drift status on AllowUpdate effective policy

• Extends progressive sync APIs to accept the effective ApplicationsSyncPolicy and threads it into status calculation. Changes spec drift detection so it only reports spec differences when updates are allowed, preventing un-actionable Waiting/Pending transitions under create-only policies.

applicationset/progressivesync/progressive_sync.go

Tests (2) +66 / -4
progressive_sync_dependencies_test.goUpdate progressive sync tests for new policy-aware signature +1/-1

Update progressive sync tests for new policy-aware signature

• Updates the ProgressiveSyncManager status update test call sites to include an ApplicationsSyncPolicy argument, aligning tests with the new API.

applicationset/controllers/progressive_sync_dependencies_test.go

specchanged_regression_test.goAdd regression coverage for no-update policies and revision changes +65/-3

Add regression coverage for no-update policies and revision changes

• Updates existing regression tests for the new status API signature and adds coverage to ensure spec drift is suppressed when updates are forbidden. Adds a dedicated test to confirm revision changes are still surfaced even under create-only policy.

applicationset/progressivesync/specchanged_regression_test.go

@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Bundle Report

Bundle size has no change ✅

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Unneeded diffConfig build 🐞 Bug ➹ Performance
Description
UpdateApplicationSetApplicationStatus now skips spec comparison unless
effectivePolicy.AllowUpdate(), but it still builds the ignore-diff DiffConfig unconditionally, which
becomes unused for create-only/create-delete policies. This adds unnecessary work and can still fail
reconciliation if DiffConfig building errors even though updates/spec comparisons are disabled by
policy.
Code

applicationset/progressivesync/progressive_sync.go[R449-452]

+		// Only report spec changes that the effective policy allows the write path to apply.
		specChanged := false
-		if desiredApp, ok := desiredAppsMap[app.Name]; ok {
+		if desiredApp, ok := desiredAppsMap[app.Name]; ok && effectivePolicy.AllowUpdate() {
			// Compare the desired spec with the current spec to detect non-Git changes
Evidence
The PR change adds an AllowUpdate() gate to the only code path that consumes diffConfig
(SpecsEquivalent), but diffConfig is still built before the loop regardless of policy.
BuildIgnoreDiffConfig calls DiffConfigBuilder.Build(), which can return an error; under no-update
policies this error becomes unnecessary because the config is unused.

applicationset/progressivesync/progressive_sync.go[396-468]
applicationset/utils/createOrUpdate.go[52-62]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`UpdateApplicationSetApplicationStatus` now gates spec comparisons behind `effectivePolicy.AllowUpdate()`, but it still builds `diffConfig` unconditionally. For no-update policies, the build is wasted and can still return an error that aborts reconciliation even though spec comparison can never run.

### Issue Context
- `diffConfig` is only used by `utils.SpecsEquivalent(...)` in the spec-change detection block.
- The PR added `&& effectivePolicy.AllowUpdate()` to that block, so for create-only/create-delete the config is unused.

### Fix Focus Areas
- applicationset/progressivesync/progressive_sync.go[400-468]

### Suggested fix
- Only call `utils.BuildIgnoreDiffConfig(...)` when `effectivePolicy.AllowUpdate()` is true (and/or right before the first possible `SpecsEquivalent` call).
- Keep behavior unchanged for update-allowed policies; for update-disallowed policies, skip building `diffConfig` entirely.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can group findings by type and pick your Finding display, from Minimal to Full

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment on lines +449 to 452
// Only report spec changes that the effective policy allows the write path to apply.
specChanged := false
if desiredApp, ok := desiredAppsMap[app.Name]; ok {
if desiredApp, ok := desiredAppsMap[app.Name]; ok && effectivePolicy.AllowUpdate() {
// Compare the desired spec with the current spec to detect non-Git changes

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. Unneeded diffconfig build 🐞 Bug ➹ Performance

UpdateApplicationSetApplicationStatus now skips spec comparison unless
effectivePolicy.AllowUpdate(), but it still builds the ignore-diff DiffConfig unconditionally, which
becomes unused for create-only/create-delete policies. This adds unnecessary work and can still fail
reconciliation if DiffConfig building errors even though updates/spec comparisons are disabled by
policy.
Agent Prompt
### Issue description
`UpdateApplicationSetApplicationStatus` now gates spec comparisons behind `effectivePolicy.AllowUpdate()`, but it still builds `diffConfig` unconditionally. For no-update policies, the build is wasted and can still return an error that aborts reconciliation even though spec comparison can never run.

### Issue Context
- `diffConfig` is only used by `utils.SpecsEquivalent(...)` in the spec-change detection block.
- The PR added `&& effectivePolicy.AllowUpdate()` to that block, so for create-only/create-delete the config is unused.

### Fix Focus Areas
- applicationset/progressivesync/progressive_sync.go[400-468]

### Suggested fix
- Only call `utils.BuildIgnoreDiffConfig(...)` when `effectivePolicy.AllowUpdate()` is true (and/or right before the first possible `SpecsEquivalent` call).
- Keep behavior unchanged for update-allowed policies; for update-disallowed policies, skip building `diffConfig` entirely.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.61%. Comparing base (777f913) to head (3399110).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #29145   +/-   ##
=======================================
  Coverage   65.61%   65.61%           
=======================================
  Files         427      427           
  Lines       60564    60565    +1     
=======================================
+ Hits        39738    39741    +3     
  Misses      17189    17189           
+ Partials     3637     3635    -2     
Flag Coverage Δ
e2e 26.83% <100.00%> (-0.05%) ⬇️
unit-tests 61.23% <66.66%> (+0.05%) ⬆️

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

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

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.

Progressive sync reports pending spec changes that a create-only policy can never apply

1 participant