Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 12 additions & 11 deletions cmd/argocd/commands/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
command := &cobra.Command{
Use: "get APPNAME",
Short: "Get application details",
Example: templates.Examples(`
Example: templates.Examples(`
# Get basic details about the application "my-app" in wide format
argocd app get my-app -o wide

Expand Down Expand Up @@ -383,7 +383,7 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com

# Get application details and display them in a tree format
argocd app get my-app --output tree

# Get application details and display them in a detailed tree format
argocd app get my-app --output tree=detailed
`),
Expand Down Expand Up @@ -541,7 +541,7 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
command := &cobra.Command{
Use: "logs APPNAME",
Short: "Get logs of application pods",
Example: templates.Examples(`
Example: templates.Examples(`
# Get logs of pods associated with the application "my-app"
argocd app logs my-app

Expand Down Expand Up @@ -855,7 +855,7 @@ func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
command := &cobra.Command{
Use: "set APPNAME",
Short: "Set application parameters",
Example: templates.Examples(`
Example: templates.Examples(`
# Set application parameters for the application "my-app"
argocd app set my-app --parameter key1=value1 --parameter key2=value2

Expand Down Expand Up @@ -2520,18 +2520,19 @@ func getResourceStates(app *argoappv1.Application, selectedResources []*argoappv
// print most resources info along with most recent operation results
if app.Status.OperationState != nil && app.Status.OperationState.SyncResult != nil {
for _, res := range app.Status.OperationState.SyncResult.Resources {
sync := string(res.HookPhase)
health := string(res.Status)
status := string(res.Status)
health := ""
key := kube.NewResourceKey(res.Group, res.Kind, res.Namespace, res.Name)
if resource, ok := resourceByKey[key]; ok && res.HookType == "" {
health = ""
if resource, ok := resourceByKey[key]; ok {
if resource.Health != nil {
health = string(resource.Health.Status)
}
sync = string(resource.Status)
}
if res.HookType != "" {
status = string(res.HookPhase)
}
states = append(states, &resourceState{
Group: res.Group, Kind: res.Kind, Namespace: res.Namespace, Name: res.Name, Status: sync, Health: health, Hook: string(res.HookType), Message: res.Message,
Group: res.Group, Kind: res.Kind, Namespace: res.Namespace, Name: res.Name, Status: status, Health: health, Hook: string(res.HookType), Message: res.Message,
})
delete(resourceByKey, kube.NewResourceKey(res.Group, res.Kind, res.Namespace, res.Name))
}
Expand Down Expand Up @@ -3484,7 +3485,7 @@ func NewApplicationRemoveSourceCommand(clientOpts *argocdclient.ClientOptions) *
Short: "Remove a source from multiple sources application.",
Example: ` # Remove the source at position 1 from application's sources. Counting starts at 1.
argocd app remove-source myapplication --source-position 1

# Remove the source named "test" from application's sources.
argocd app remove-source myapplication --source-name test`,
Run: func(c *cobra.Command, args []string) {
Expand Down
4 changes: 3 additions & 1 deletion controller/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, project *v1alp
state.Message = fmt.Sprintf("Failed to generate sync ID: %v", err)
return
}
logEntry := log.WithFields(applog.GetAppLogFields(app)).WithField("syncId", syncId)
logger := logutils.NewWithCurrentConfig()
logger.SetLevel(log.TraceLevel)
logEntry := logger.WithFields(applog.GetAppLogFields(app)).WithField("syncId", syncId)

if state.Operation.Sync == nil {
state.Phase = common.OperationError
Expand Down
2 changes: 1 addition & 1 deletion docs/user-guide/commands/argocd_app_remove-source.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 28 additions & 28 deletions docs/user-guide/sync-waves.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ Sync phases and hooks define when resources are applied such as before or after

Argo CD has the following hook types:

| Hook | Description |
|------|-------------|
| `PreSync` | Executes prior to the application of the manifests. |
| `Sync` | Executes after all `PreSync` hooks completed and were successful, at the same time as the application of the manifests. |
| `Skip` | Indicates to Argo CD to skip the application of the manifest. |
| `PostSync` | Executes after all `Sync` hooks completed and were successful, a successful application, and all resources in a `Healthy` state. |
| `SyncFail` | Executes when the sync operation fails. |
| `PostDelete` | Executes after all Application resources are deleted. _Available starting in v2.10._ |
| Hook | Description |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| `PreSync` | Executes prior to the application of the manifests. |
| `Sync` | Executes after all `PreSync` hooks completed and were successful, at the same time as the application of the manifests. |
| `Skip` | Indicates to Argo CD to skip the application of the manifest. |
| `PostSync` | Executes after all `Sync` hooks completed and were successful, a successful application, and all resources in a `Healthy` state. |
| `SyncFail` | Executes when the sync operation fails. |
| `PostDelete` | Executes after all Application resources are deleted. _Available starting in v2.10._ |

Adding the argocd.argoproj.io/hook annotation to a resource will assign it to a specific phase. During a Sync operation, Argo CD will apply the resource during the appropriate phase of the deployment. Hooks can be any type of Kubernetes resource kind, but tend to be Pod, Job or Argo Workflows. Multiple hooks can be specified as a comma separated list.

Expand Down Expand Up @@ -40,13 +40,12 @@ Argo CD offers several methods to clean up hooks and decide how much history wil
In the most basic case you can use the argocd.argoproj.io/hook-delete-policy to decide when a hook will be deleted.
This can take the following values:

| Policy | Description |
|--------|-------------|
| `HookSucceeded` | The hook resource is deleted after the hook succeeded (e.g. Job/Workflow completed successfully). |
| `HookFailed` | The hook resource is deleted after the hook failed. |
| Policy | Description |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `HookSucceeded` | The hook resource is deleted if the sync succeeds (e.g. All `PreSync` completed successfully). |
| `HookFailed` | The hook resource is deleted after the sync fails. |
| `BeforeHookCreation` | Any existing hook resource is deleted before the new one is created (since v1.3). It is meant to be used with `/metadata/name`. |


## How sync waves work?

Argo CD also offers an alternative method of changing the sync order of resources. These are sync waves. They are defined by the argocd.argoproj.io/sync-wave annotation. The value is an integer that defines the ordering (ArgoCD will start deploying from the lowest number and finish with the highest number).
Expand Down Expand Up @@ -99,7 +98,7 @@ Specify the wave using the following annotation:
```yaml
metadata:
annotations:
argocd.argoproj.io/sync-wave: "5"
argocd.argoproj.io/sync-wave: '5'
```

Hooks and resources are assigned to wave zero by default. The wave can be negative, so you can create a wave that runs before all other resources.
Expand Down Expand Up @@ -138,6 +137,7 @@ spec:
```

The following example runs a db migration command before the main sync operation (also in wave -1):

```yaml
apiVersion: batch/v1
kind: Job
Expand Down Expand Up @@ -173,20 +173,20 @@ spec:

Upgrading ingress-nginx controller (managed by helm) with ArgoCD 2.x sometimes fails to work resulting in:

.|.
-|-
OPERATION|Sync
PHASE|Running
MESSAGE|waiting for completion of hook batch/Job/ingress-nginx-admission-create

.|.
-|-
KIND |batch/v1/Job
NAMESPACE|ingress-nginx
NAME |ingress-nginx-admission-create
STATUS |Running
HOOK |PreSync
MESSAGE |Pending deletion
| . | . |
| --------- | ----------------------------------------------------------------------- |
| OPERATION | Sync |
| PHASE | Running |
| MESSAGE | waiting for completion of hook batch/Job/ingress-nginx-admission-create |

| . | . |
| --------- | ------------------------------ |
| KIND | batch/v1/Job |
| NAMESPACE | ingress-nginx |
| NAME | ingress-nginx-admission-create |
| STATUS | Running |
| HOOK | PreSync |
| MESSAGE | Pending deletion |

To work around this, a helm user can add:

Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ require (
)

replace (
github.com/argoproj/gitops-engine => github.com/agaudreault/gitops-engine v0.7.1-0.20250902171136-ef00d914b5e3

github.com/golang/protobuf => github.com/golang/protobuf v1.5.4
github.com/grpc-ecosystem/grpc-gateway => github.com/grpc-ecosystem/grpc-gateway v1.16.0
golang.org/x/tools => golang.org/x/tools v0.35.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20240116134246-a8cbe886bab0 h1:z
github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20240116134246-a8cbe886bab0/go.mod h1:rjP7sIipbZcagro/6TCk6X0ZeFT2eyudH5+fve/cbBA=
github.com/TomOnTime/utfutil v1.0.0 h1:/0Ivgo2OjXJxo8i7zgvs7ewSFZMLwCRGm3P5Umowb90=
github.com/TomOnTime/utfutil v1.0.0/go.mod h1:l9lZmOniizVSuIliSkEf87qivMRlSNzbdBFKjuLRg1c=
github.com/agaudreault/gitops-engine v0.7.1-0.20250902171136-ef00d914b5e3 h1:3n8H+yRovy2hzmRCn4qLrNRIASf9hKkqwxSS4//OCpY=
github.com/agaudreault/gitops-engine v0.7.1-0.20250902171136-ef00d914b5e3/go.mod h1:aIBEG3ohgaC1gh/sw2On6knkSnXkqRLDoBj234Dqczw=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
Expand All @@ -113,8 +115,6 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/appscode/go v0.0.0-20191119085241-0887d8ec2ecc/go.mod h1:OawnOmAL4ZX3YaPdN+8HTNwBveT1jMsqP74moa9XUbE=
github.com/argoproj/gitops-engine v0.7.1-0.20250724135328-38f73a75c3cf h1:T6GGt8vYN2aRNnOwdY3DIzMX1AhGIEo+8SNhSlNBdvs=
github.com/argoproj/gitops-engine v0.7.1-0.20250724135328-38f73a75c3cf/go.mod h1:aIBEG3ohgaC1gh/sw2On6knkSnXkqRLDoBj234Dqczw=
github.com/argoproj/notifications-engine v0.4.1-0.20250710034121-3ec18d4536a7 h1:DwrJWH9kySJgaJ6n5U543QSeFTNdMHDz5Iy+goyfQUw=
github.com/argoproj/notifications-engine v0.4.1-0.20250710034121-3ec18d4536a7/go.mod h1:d1RazGXWvKRFv9//rg4MRRR7rbvbE7XLgTSMT5fITTE=
github.com/argoproj/pkg v0.13.6 h1:36WPD9MNYECHcO1/R1pj6teYspiK7uMQLCgLGft2abM=
Expand Down
20 changes: 12 additions & 8 deletions test/e2e/admin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,18 @@ func TestBackupExportImport(t *testing.T) {
appctx := appfixture.GivenWithSameState(t)

// Create application in test namespace
var appTestNamespace Application
var appOtherNamespace Application
appctx.
Path(guestbookPath).
Name("exported-app1").
When().
CreateApp().
Then().
And(func(app *Application) {
assert.Equal(t, "exported-app1", app.Name)
assert.Equal(t, fixture.TestNamespace(), app.Namespace)
assert.Equal(t, appctx.AppName(), app.Name)
assert.Equal(t, appctx.AppNamespace(), app.Namespace)
appTestNamespace = *app
})

// Create app in other namespace
Expand All @@ -42,8 +45,9 @@ func TestBackupExportImport(t *testing.T) {
CreateApp().
Then().
And(func(app *Application) {
assert.Equal(t, "exported-app-other-namespace", app.Name)
assert.Equal(t, fixture.AppNamespace(), app.Namespace)
assert.Equal(t, appctx.AppName(), app.Name)
assert.Equal(t, appctx.AppNamespace(), app.Namespace)
appOtherNamespace = *app
})

ctx.
Expand All @@ -57,8 +61,8 @@ func TestBackupExportImport(t *testing.T) {
AndExportedResources(func(exportResources *ExportedResources, err error) {
require.NoError(t, err, "export format not valid")
assert.True(t, exportResources.HasResource(kube.NewResourceKey("", "ConfigMap", "", "argocd-cm")), "argocd-cm not found in export")
assert.True(t, exportResources.HasResource(kube.NewResourceKey(ApplicationSchemaGroupVersionKind.Group, ApplicationSchemaGroupVersionKind.Kind, "", "exported-app1")), "test namespace application not in export")
assert.True(t, exportResources.HasResource(kube.NewResourceKey(ApplicationSchemaGroupVersionKind.Group, ApplicationSchemaGroupVersionKind.Kind, fixture.AppNamespace(), "exported-app-other-namespace")), "app namespace application not in export")
assert.True(t, exportResources.HasResource(kube.NewResourceKey(ApplicationSchemaGroupVersionKind.Group, ApplicationSchemaGroupVersionKind.Kind, "", appTestNamespace.GetName())), "test namespace application not in export")
assert.True(t, exportResources.HasResource(kube.NewResourceKey(ApplicationSchemaGroupVersionKind.Group, ApplicationSchemaGroupVersionKind.Kind, appOtherNamespace.GetNamespace(), appOtherNamespace.GetName())), "app namespace application not in export")
})

// Test import - clean state
Expand All @@ -70,9 +74,9 @@ func TestBackupExportImport(t *testing.T) {
Then().
AndCLIOutput(func(_ string, err error) {
require.NoError(t, err, "import finished with error")
_, err = fixture.AppClientset.ArgoprojV1alpha1().Applications(fixture.TestNamespace()).Get(t.Context(), "exported-app1", metav1.GetOptions{})
_, err = fixture.AppClientset.ArgoprojV1alpha1().Applications(appTestNamespace.GetNamespace()).Get(t.Context(), appTestNamespace.GetName(), metav1.GetOptions{})
require.NoError(t, err, "failed getting test namespace application after import")
_, err = fixture.AppClientset.ArgoprojV1alpha1().Applications(fixture.AppNamespace()).Get(t.Context(), "exported-app-other-namespace", metav1.GetOptions{})
_, err = fixture.AppClientset.ArgoprojV1alpha1().Applications(appOtherNamespace.GetNamespace()).Get(t.Context(), appOtherNamespace.GetName(), metav1.GetOptions{})
require.NoError(t, err, "failed getting app namespace application after import")
})
}
68 changes: 38 additions & 30 deletions test/e2e/app_management_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,7 @@ func TestAppWithSecrets(t *testing.T) {
assert.Empty(t, diffOutput)

// make sure resource update error does not print secret details
_, err = fixture.RunCli("app", "patch-resource", "test-app-with-secrets", "--resource-name", "test-secret",
_, err = fixture.RunCli("app", "patch-resource", app.Name, "--resource-name", "test-secret",
"--kind", "Secret", "--patch", `{"op": "add", "path": "/data", "value": "hello"}'`,
"--patch-type", "application/json-patch+json")
require.ErrorContains(t, err, fmt.Sprintf("failed to patch Secret %s/test-secret", fixture.DeploymentNamespace()))
Expand Down Expand Up @@ -1534,10 +1534,9 @@ func assertResourceActions(t *testing.T, appName string, successful bool) {

func TestPermissions(t *testing.T) {
appCtx := Given(t)
projName := "argo-project"
projActions := projectFixture.
GivenWithSameState(t).
Name(projName).
projCtx := projectFixture.GivenWithSameState(t)
projActions := projCtx.
Name("argo-project").
When().
Create()

Expand All @@ -1546,7 +1545,7 @@ func TestPermissions(t *testing.T) {

appCtx.
Path("guestbook-logs").
Project(projName).
Project(projCtx.GetName()).
When().
IgnoreErrors().
// ensure app is not created if project permissions are missing
Expand Down Expand Up @@ -1604,8 +1603,8 @@ func TestPermissions(t *testing.T) {
Refresh(RefreshTypeNormal).
Then().
// make sure application resource actiions are failing
And(func(_ *Application) {
assertResourceActions(t, "test-permissions", false)
And(func(a *Application) {
assertResourceActions(t, a.GetName(), false)
})
}

Expand Down Expand Up @@ -3000,37 +2999,46 @@ func TestInstallationID(t *testing.T) {

func TestDeletionConfirmation(t *testing.T) {
ctx := Given(t)
ctx.
And(func() {
_, err := fixture.KubeClientset.CoreV1().ConfigMaps(fixture.DeploymentNamespace()).Create(
t.Context(), &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "test-configmap",
Labels: map[string]string{
common.LabelKeyAppInstance: ctx.AppName(),
},
Annotations: map[string]string{
AnnotationSyncOptions: "Prune=confirm",
},
ctx.And(func() {
_, err := fixture.KubeClientset.CoreV1().ConfigMaps(fixture.DeploymentNamespace()).Create(
t.Context(), &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "test-configmap",
Labels: map[string]string{
common.LabelKeyAppInstance: ctx.AppName(),
},
}, metav1.CreateOptions{})
require.NoError(t, err)
}).
Annotations: map[string]string{
AnnotationSyncOptions: "Prune=confirm",
},
},
}, metav1.CreateOptions{})
require.NoError(t, err)
}).
Path(guestbookPath).
Async(true).
When().
PatchFile("guestbook-ui-deployment.yaml", `[{ "op": "add", "path": "/metadata/annotations", "value": { "argocd.argoproj.io/sync-options": "Delete=confirm" }}]`).
CreateApp().Sync().
Then().Expect(OperationPhaseIs(OperationRunning)).
When().ConfirmDeletion().
Then().Expect(OperationPhaseIs(OperationSucceeded)).
When().Delete(true).
CreateApp().
Sync().
Then().
Expect(OperationPhaseIs(OperationRunning)).
When().
ConfirmDeletion().
Then().
Expect(OperationPhaseIs(OperationSucceeded)).
Given().
// Make sure to sleep a bit so the existing confirmed pruning annotation is after the deletion request
Sleep(3).
When().
Delete(true).
Then().
And(func(app *Application) {
assert.NotNil(t, app.DeletionTimestamp)
}).
When().ConfirmDeletion().
Then().Expect(DoesNotExist())
When().
ConfirmDeletion().
Then().
Expect(DoesNotExist())
}

func TestLastTransitionTimeUnchangedError(t *testing.T) {
Expand Down
Loading
Loading