github.com/argoproj/argo-cd/v3@v3.2.1/test/e2e/app_autosync_test.go (about) 1 package e2e 2 3 import ( 4 "fmt" 5 "testing" 6 "time" 7 8 . "github.com/argoproj/gitops-engine/pkg/sync/common" 9 "github.com/stretchr/testify/assert" 10 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 "k8s.io/apimachinery/pkg/types" 12 "k8s.io/utils/ptr" 13 14 . "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 15 "github.com/argoproj/argo-cd/v3/test/e2e/fixture" 16 . "github.com/argoproj/argo-cd/v3/test/e2e/fixture/app" 17 "github.com/argoproj/argo-cd/v3/util/errors" 18 ) 19 20 func TestAutoSyncSelfHealDisabled(t *testing.T) { 21 Given(t). 22 Path(guestbookPath). 23 When(). 24 // app should be auto-synced once created 25 CreateFromFile(func(app *Application) { 26 app.Spec.SyncPolicy = &SyncPolicy{Automated: &SyncPolicyAutomated{SelfHeal: false}} 27 }). 28 Then(). 29 Expect(SyncStatusIs(SyncStatusCodeSynced)). 30 // app should be auto-synced if git change detected 31 When(). 32 PatchFile("guestbook-ui-deployment.yaml", `[{"op": "replace", "path": "/spec/revisionHistoryLimit", "value": 1}]`). 33 Refresh(RefreshTypeNormal). 34 Then(). 35 Expect(SyncStatusIs(SyncStatusCodeSynced)). 36 // app should not be auto-synced if k8s change detected 37 When(). 38 And(func() { 39 errors.NewHandler(t).FailOnErr(fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Patch(t.Context(), 40 "guestbook-ui", types.MergePatchType, []byte(`{"spec": {"revisionHistoryLimit": 0}}`), metav1.PatchOptions{})) 41 }). 42 Refresh(RefreshTypeNormal). 43 Then(). 44 Expect(SyncStatusIs(SyncStatusCodeOutOfSync)) 45 } 46 47 func TestAutoSyncSelfHealEnabled(t *testing.T) { 48 Given(t). 49 Path(guestbookPath). 50 When(). 51 // app should be auto-synced once created 52 CreateFromFile(func(app *Application) { 53 app.Spec.SyncPolicy = &SyncPolicy{ 54 Automated: &SyncPolicyAutomated{SelfHeal: true}, 55 Retry: &RetryStrategy{Limit: 0}, 56 } 57 }). 58 Then(). 59 Expect(OperationPhaseIs(OperationSucceeded)). 60 Expect(SyncStatusIs(SyncStatusCodeSynced)). 61 When(). 62 // app should be auto-synced once k8s change detected 63 And(func() { 64 errors.NewHandler(t).FailOnErr(fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Patch(t.Context(), 65 "guestbook-ui", types.MergePatchType, []byte(`{"spec": {"revisionHistoryLimit": 0}}`), metav1.PatchOptions{})) 66 }). 67 Refresh(RefreshTypeNormal). 68 Then(). 69 Expect(OperationPhaseIs(OperationSucceeded)). 70 Expect(SyncStatusIs(SyncStatusCodeSynced)). 71 When(). 72 // app should be attempted to auto-synced once and marked with error after failed attempt detected 73 PatchFile("guestbook-ui-deployment.yaml", `[{"op": "replace", "path": "/spec/revisionHistoryLimit", "value": "badValue"}]`). 74 Refresh(RefreshTypeNormal). 75 Then(). 76 Expect(OperationPhaseIs(OperationFailed)). 77 When(). 78 // Trigger refresh again to make sure controller notices previously failed sync attempt before expectation timeout expires 79 Refresh(RefreshTypeNormal). 80 Then(). 81 Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). 82 Expect(Condition(ApplicationConditionSyncError, "Failed last sync attempt")). 83 When(). 84 // SyncError condition should be removed after successful sync 85 PatchFile("guestbook-ui-deployment.yaml", `[{"op": "replace", "path": "/spec/revisionHistoryLimit", "value": 1}]`). 86 Refresh(RefreshTypeNormal). 87 Then(). 88 Expect(OperationPhaseIs(OperationSucceeded)). 89 When(). 90 // Trigger refresh twice to make sure controller notices successful attempt and removes condition 91 Refresh(RefreshTypeNormal). 92 Then(). 93 Expect(SyncStatusIs(SyncStatusCodeSynced)). 94 And(func(app *Application) { 95 assert.Empty(t, app.Status.Conditions) 96 }) 97 } 98 99 // TestAutoSyncRetryAndRefreshEnabled verifies that auto-sync+refresh picks up new commits automatically 100 func TestAutoSyncRetryAndRefreshEnabled(t *testing.T) { 101 Given(t). 102 Path(guestbookPath). 103 When(). // I create an app with auto-sync and Refresh 104 CreateFromFile(func(app *Application) { 105 app.Spec.SyncPolicy = &SyncPolicy{ 106 Automated: &SyncPolicyAutomated{}, 107 Retry: &RetryStrategy{ 108 Limit: -1, 109 Refresh: true, 110 Backoff: &Backoff{ 111 Duration: time.Second.String(), 112 Factor: ptr.To(int64(1)), 113 MaxDuration: time.Second.String(), 114 }, 115 }, 116 } 117 }). 118 Then(). // It should auto-sync correctly 119 Expect(OperationPhaseIs(OperationSucceeded)). 120 Expect(SyncStatusIs(SyncStatusCodeSynced)). 121 Expect(NoConditions()). 122 When(). // Auto-sync encounters broken commit 123 PatchFile("guestbook-ui-deployment.yaml", `[{"op": "replace", "path": "/spec/revisionHistoryLimit", "value": "badValue"}]`). 124 Refresh(RefreshTypeNormal). 125 Then(). // It should keep on trying to sync it 126 Expect(OperationPhaseIs(OperationRunning)). 127 Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). 128 Expect(OperationRetriedMinimumTimes(1)). 129 When(). // I push a fixed commit (while auto-sync in progress) 130 PatchFile("guestbook-ui-deployment.yaml", `[{"op": "replace", "path": "/spec/revisionHistoryLimit", "value": 42}]`). 131 Refresh(RefreshTypeNormal). 132 Then(). 133 // Argo CD should pick it up and sync it successfully 134 Expect(OperationPhaseIs(OperationSucceeded)). 135 Expect(SyncStatusIs(SyncStatusCodeSynced)) 136 } 137 138 // TestAutoSyncRetryAndRefreshEnabled verifies that auto-sync+refresh picks up new commits automatically on the original source 139 // at the time the sync was triggered 140 func TestAutoSyncRetryAndRefreshEnabledChangedSource(t *testing.T) { 141 Given(t). 142 Path(guestbookPath). 143 When(). // I create an app with auto-sync and Refresh 144 CreateFromFile(func(app *Application) { 145 app.Spec.SyncPolicy = &SyncPolicy{ 146 Automated: &SyncPolicyAutomated{}, 147 Retry: &RetryStrategy{ 148 Limit: -1, // Repeat forever 149 Refresh: true, 150 Backoff: &Backoff{ 151 Duration: time.Second.String(), 152 Factor: ptr.To(int64(1)), 153 MaxDuration: time.Second.String(), 154 }, 155 }, 156 } 157 }). 158 Then(). // It should auto-sync correctly 159 Expect(OperationPhaseIs(OperationSucceeded)). 160 Expect(SyncStatusIs(SyncStatusCodeSynced)). 161 Expect(NoConditions()). 162 When(). // Auto-sync encounters broken commit 163 PatchFile("guestbook-ui-deployment.yaml", `[{"op": "replace", "path": "/spec/revisionHistoryLimit", "value": "badValue"}]`). 164 Refresh(RefreshTypeNormal). 165 Then(). // It should keep on trying to sync it 166 Expect(OperationPhaseIs(OperationRunning)). 167 Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). 168 Expect(OperationRetriedMinimumTimes(1)). 169 When(). 170 PatchApp(`[{"op": "add", "path": "/spec/source/path", "value": "failure-during-sync"}]`). 171 // push a fixed commit on HEAD branch 172 PatchFile("guestbook-ui-deployment.yaml", `[{"op": "replace", "path": "/spec/revisionHistoryLimit", "value": 42}]`). 173 Refresh(RefreshTypeNormal). 174 Then(). 175 Expect(Status(func(status ApplicationStatus) (bool, string) { 176 // Validate that the history contains the sync to the previous sources 177 // The history will only contain successful sync 178 if len(status.History) != 2 { 179 return false, "expected len to be 2" 180 } 181 if status.History[1].Source.Path != guestbookPath { 182 return false, fmt.Sprintf("expected source path to be '%s'", guestbookPath) 183 } 184 return true, "" 185 })) 186 }