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  }