Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e4b8acc
fix: syncing to a different revision should be considered override
schraax May 3, 2025
4bed346
fix linter errors
schraax May 3, 2025
6a218ab
revert changes to server/settings.go, do not want to expose. more cov…
schraax May 4, 2025
bd6f3c0
linting...
schraax May 4, 2025
c99469d
and lint again, sorry
schraax May 4, 2025
f47fc77
fix: typos
schraax May 6, 2025
d4ce0ab
fix: reword docs follwoing review recommendation
schraax May 18, 2025
e1dd9ce
Merge branch 'master' into fix/diverging-revision-treated-as-override2
schraax May 18, 2025
e11c57a
test docstrings were switched
schraax May 19, 2025
55b2337
fix: remove docs from 3.1 version
schraax Jul 3, 2025
4620d72
fix: rephrase docs
schraax Jul 3, 2025
cda70bc
refacor settings test
schraax Jul 10, 2025
53ecc5a
fix: split tests
schraax Jul 10, 2025
20c815c
Merge branch 'master' into fix/diverging-revision-treated-as-override2
schraax Jul 11, 2025
567bb8b
fix : linter errors
schraax Jul 12, 2025
6ad8349
duh
schraax Jul 12, 2025
bdb36ca
fix: renmae the flag, to make intent clearer
schraax Jul 13, 2025
e0f1cc3
Merge branch 'master' into fix/diverging-revision-treated-as-override2
schraax Aug 25, 2025
d32da97
Merge branch 'master' into fix/diverging-revision-treated-as-override2
schraax Sep 27, 2025
7da1673
Merge branch 'master' into fix/diverging-revision-treated-as-override2
schraax Sep 29, 2025
699879a
Merge branch 'master' into fix/diverging-revision-treated-as-override2
schraax Oct 3, 2025
9d5aa62
Merge branch 'master' into fix/diverging-revision-treated-as-override2
schraax Oct 3, 2025
c7b1ec5
fix: rework as suggested by @agaudreault
schraax Oct 6, 2025
e01aa9c
Merge branch 'master' into fix/diverging-revision-treated-as-override2
schraax Oct 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/operator-manual/argocd-cm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,11 @@ data:
# application.sync.impersonation.enabled enables application sync to use a custom service account, via impersonation. This allows decoupling sync from control-plane service account.
application.sync.impersonation.enabled: "false"

# If true, passing passing a different revision from the one given in the application when syncing requires the `override` privilege.
# The current default setting up to now (`false`) requires only `sync` privilege for syncing to a different revision.
# We highly recommend that this be set to `true`. The next major release will set the default to be `true`.
application.sync.requireOverridePrivilegeForRevisionSync: "true"

### SourceHydrator commit message template.
# This template iterates through the fields in the `.metadata` object,
# and formats them based on their type (map, array, or primitive values).
Expand All @@ -458,3 +463,4 @@ data:
{{- if .metadata.author }}
Co-authored-by: {{ .metadata.author }}
{{- end }}

16 changes: 14 additions & 2 deletions docs/operator-manual/rbac.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,20 @@ p, example-user, applications, action/*, default/*, allow

#### The `override` action

When granted along with the `sync` action, the override action will allow a user to synchronize local manifests to the Application.
These manifests will be used instead of the configured source, until the next sync is performed.
The `override` action privilege can be used to allow passing arbitrary manifests or different revisions when syncing an `Application`. This can e.g. be used for development or testing purposes.

**Attention:** This allows users to completely change/delete the deployed resources of the application.

While the `sync` action privilege gives the right to synchronize the objects in the cluster to the desired state as defined in the `Application` Object, the `override` action privilege will allow a user to synchronize arbitrary local manifests to the Application. These manifests will be used _instead of_ the configured source, until the next sync is performed. After performing such a override sync, the application will most probably be OutOfSync with the state defined via the `Application` object.
It is not possible to perform an `override` sync when auto-sync is enabled.

New since v3.2:

When `application.sync.requireOverridePrivilegeForRevisionSync: 'true'` is set in the `argcd-cm` configmap,
passing a revision when syncing an `Application` is also considered as an `override`, to prevent synchronizing to arbitrary revisions other than the revision(s) given in the `Application` object. Similar as synching to an arbitrary yaml manifest, syncing to a different revision/branch/commit will also bring the controlled objects to a state differing, and thus OufOfSync from the state as defined in the `Application`.

The default setting of this flag is 'false', to prevent breaking changes in existing installations. It is recommended to set this setting to 'true' and only grant the `override` privilege per AppProject to the users that actually need this behavior.


### The `applicationsets` resource

Expand Down
44 changes: 34 additions & 10 deletions server/application/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -2113,22 +2113,36 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR
}

func (s *Server) resolveSourceRevisions(ctx context.Context, a *v1alpha1.Application, syncReq *application.ApplicationSyncRequest) (string, string, []string, []string, error) {
requireOverridePrivilegeForRevisionSync, err := s.settingsMgr.RequireOverridePrivilegeForRevisionSync()
if err != nil {
// give up, and return the error
return "", "", nil, nil,
fmt.Errorf("error getting setting 'RequireOverridePrivilegeForRevisionSync' from configmap: : %w", err)
}
if a.Spec.HasMultipleSources() {
numOfSources := int64(len(a.Spec.GetSources()))
sourceRevisions := make([]string, numOfSources)
displayRevisions := make([]string, numOfSources)

sources := a.Spec.GetSources()
desiredRevisions := make([]string, numOfSources)
for i, pos := range syncReq.SourcePositions {
if pos <= 0 || pos > numOfSources {
return "", "", nil, nil, errors.New("source position is out of range")
}
sources[pos-1].TargetRevision = syncReq.Revisions[i]
}
for index, source := range sources {
if a.Spec.SyncPolicy != nil && a.Spec.SyncPolicy.IsAutomatedSyncEnabled() && !syncReq.GetDryRun() {
if text.FirstNonEmpty(a.Spec.GetSources()[index].TargetRevision, "HEAD") != text.FirstNonEmpty(source.TargetRevision, "HEAD") {
return "", "", nil, nil, status.Errorf(codes.FailedPrecondition, "Cannot sync source %s to %s: auto-sync currently set to %s", source.RepoURL, source.TargetRevision, a.Spec.Sources[index].TargetRevision)
desiredRevisions[pos-1] = syncReq.Revisions[i]
}
for index, desiredRevision := range desiredRevisions {
if desiredRevision != "" && desiredRevision != text.FirstNonEmpty(a.Spec.GetSources()[index].TargetRevision, "HEAD") {
// User is trying to sync to a different revision than the ones specified in the app sources
// Enforce that they have the 'override' privilege if the setting is enabled
if requireOverridePrivilegeForRevisionSync {
if err := s.enf.EnforceErr(ctx.Value("claims"), rbac.ResourceApplications, rbac.ActionOverride, a.RBACName(s.ns)); err != nil {
return "", "", nil, nil, err
}
}
if a.Spec.SyncPolicy != nil && a.Spec.SyncPolicy.IsAutomatedSyncEnabled() && !syncReq.GetDryRun() {
return "", "", nil, nil, status.Errorf(codes.FailedPrecondition,
"Cannot sync source %s to %s: auto-sync currently set to %s",
a.Spec.GetSources()[index].RepoURL, desiredRevision, a.Spec.Sources[index].TargetRevision)
}
}
revision, displayRevision, err := s.resolveRevision(ctx, a, syncReq, index)
Expand All @@ -2141,8 +2155,18 @@ func (s *Server) resolveSourceRevisions(ctx context.Context, a *v1alpha1.Applica
return "", "", sourceRevisions, displayRevisions, nil
}
source := a.Spec.GetSource()
if a.Spec.SyncPolicy != nil && a.Spec.SyncPolicy.IsAutomatedSyncEnabled() && !syncReq.GetDryRun() {
if syncReq.GetRevision() != "" && syncReq.GetRevision() != text.FirstNonEmpty(source.TargetRevision, "HEAD") {
if syncReq.GetRevision() != "" &&
syncReq.GetRevision() != text.FirstNonEmpty(source.TargetRevision, "HEAD") {
// User is trying to sync to a different revision than the one specified in the app spec
// Enforce that they have the 'override' privilege if the setting is enabled
if requireOverridePrivilegeForRevisionSync {
if err := s.enf.EnforceErr(ctx.Value("claims"), rbac.ResourceApplications, rbac.ActionOverride, a.RBACName(s.ns)); err != nil {
return "", "", nil, nil, err
}
}
if a.Spec.SyncPolicy != nil &&
a.Spec.SyncPolicy.IsAutomatedSyncEnabled() && !syncReq.GetDryRun() {
// If the app has auto-sync enabled, we cannot allow syncing to a different revision
return "", "", nil, nil, status.Errorf(codes.FailedPrecondition, "Cannot sync to %s: auto-sync currently set to %s", syncReq.GetRevision(), source.TargetRevision)
}
}
Expand Down
Loading
Loading