github.com/argoproj/argo-cd/v3@v3.2.1/test/e2e/git_test.go (about)

     1  package e2e
     2  
     3  import (
     4  	"context"
     5  	"strings"
     6  	"testing"
     7  	"time"
     8  
     9  	corev1 "k8s.io/api/core/v1"
    10  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    11  	"k8s.io/apimachinery/pkg/types"
    12  	"k8s.io/apimachinery/pkg/util/wait"
    13  
    14  	"github.com/stretchr/testify/require"
    15  
    16  	. "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
    17  	"github.com/argoproj/argo-cd/v3/test/e2e/fixture"
    18  	. "github.com/argoproj/argo-cd/v3/test/e2e/fixture/app"
    19  	"github.com/argoproj/argo-cd/v3/util/errors"
    20  )
    21  
    22  func TestGitSemverResolutionNotUsingConstraint(t *testing.T) {
    23  	Given(t).
    24  		Path("deployment").
    25  		CustomSSHKnownHostsAdded().
    26  		SSHRepoURLAdded(true).
    27  		RepoURLType(fixture.RepoURLTypeSSH).
    28  		Revision("v0.1.0").
    29  		When().
    30  		AddTag("v0.1.0").
    31  		CreateApp().
    32  		Sync().
    33  		Then().
    34  		Expect(SyncStatusIs(SyncStatusCodeSynced))
    35  }
    36  
    37  func TestGitSemverResolutionNotUsingConstraintWithLeadingZero(t *testing.T) {
    38  	Given(t).
    39  		Path("deployment").
    40  		CustomSSHKnownHostsAdded().
    41  		SSHRepoURLAdded(true).
    42  		RepoURLType(fixture.RepoURLTypeSSH).
    43  		Revision("0.1.0").
    44  		When().
    45  		AddTag("0.1.0").
    46  		CreateApp().
    47  		Sync().
    48  		Then().
    49  		Expect(SyncStatusIs(SyncStatusCodeSynced))
    50  }
    51  
    52  func TestGitSemverResolutionUsingConstraint(t *testing.T) {
    53  	Given(t).
    54  		Path("deployment").
    55  		CustomSSHKnownHostsAdded().
    56  		SSHRepoURLAdded(true).
    57  		RepoURLType(fixture.RepoURLTypeSSH).
    58  		Revision("v0.1.*").
    59  		When().
    60  		AddTag("v0.1.0").
    61  		CreateApp().
    62  		Sync().
    63  		Then().
    64  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
    65  		When().
    66  		PatchFile("deployment.yaml", `[
    67  	{"op": "replace", "path": "/metadata/name", "value": "new-app"},
    68  	{"op": "replace", "path": "/spec/replicas", "value": 1}
    69  ]`).
    70  		AddTag("v0.1.2").
    71  		Sync().
    72  		Then().
    73  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
    74  		Expect(Pod(func(p corev1.Pod) bool { return strings.HasPrefix(p.Name, "new-app") }))
    75  }
    76  
    77  func TestGitSemverResolutionUsingConstraintWithLeadingZero(t *testing.T) {
    78  	Given(t).
    79  		Path("deployment").
    80  		CustomSSHKnownHostsAdded().
    81  		SSHRepoURLAdded(true).
    82  		RepoURLType(fixture.RepoURLTypeSSH).
    83  		Revision("0.1.*").
    84  		When().
    85  		AddTag("0.1.0").
    86  		CreateApp().
    87  		Sync().
    88  		Then().
    89  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
    90  		When().
    91  		PatchFile("deployment.yaml", `[
    92  	{"op": "replace", "path": "/metadata/name", "value": "new-app"},
    93  	{"op": "replace", "path": "/spec/replicas", "value": 1}
    94  ]`).
    95  		AddTag("0.1.2").
    96  		Sync().
    97  		Then().
    98  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
    99  		Expect(Pod(func(p corev1.Pod) bool { return strings.HasPrefix(p.Name, "new-app") }))
   100  }
   101  
   102  func TestAnnotatedTagInStatusSyncRevision(t *testing.T) {
   103  	Given(t).
   104  		Path(guestbookPath).
   105  		When().
   106  		// Create annotated tag name 'annotated-tag'
   107  		AddAnnotatedTag("annotated-tag", "my-generic-tag-message").
   108  		// Create Application targeting annotated-tag, with automatedSync: true
   109  		CreateFromFile(func(app *Application) {
   110  			app.Spec.Source.TargetRevision = "annotated-tag"
   111  			app.Spec.SyncPolicy = &SyncPolicy{Automated: &SyncPolicyAutomated{Prune: true, SelfHeal: false}}
   112  		}).
   113  		Then().
   114  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   115  		And(func(app *Application) {
   116  			annotatedTagIDOutput, err := fixture.Run(fixture.TmpDir+"/testdata.git", "git", "show-ref", "annotated-tag")
   117  			require.NoError(t, err)
   118  			require.NotEmpty(t, annotatedTagIDOutput)
   119  			// example command output:
   120  			// "569798c430515ffe170bdb23e3aafaf8ae24b9ff refs/tags/annotated-tag"
   121  			annotatedTagIDFields := strings.Fields(string(annotatedTagIDOutput))
   122  			require.Len(t, annotatedTagIDFields, 2)
   123  
   124  			targetCommitID, err := fixture.Run(fixture.TmpDir+"/testdata.git", "git", "rev-parse", "--verify", "annotated-tag^{commit}")
   125  			// example command output:
   126  			// "bcd35965e494273355265b9f0bf85075b6bc5163"
   127  			require.NoError(t, err)
   128  			require.NotEmpty(t, targetCommitID)
   129  
   130  			require.NotEmpty(t, app.Status.Sync.Revision, "revision in sync status should be set by sync operation")
   131  
   132  			require.NotEqual(t, app.Status.Sync.Revision, annotatedTagIDFields[0], "revision should not match the annotated tag id")
   133  			require.Equal(t, app.Status.Sync.Revision, strings.TrimSpace(string(targetCommitID)), "revision SHOULD match the target commit SHA")
   134  		})
   135  }
   136  
   137  // Test updates to K8s resources should not trigger a self-heal when self-heal is false.
   138  func TestAutomatedSelfHealingAgainstAnnotatedTag(t *testing.T) {
   139  	Given(t).
   140  		Path(guestbookPath).
   141  		When().
   142  		AddAnnotatedTag("annotated-tag", "my-generic-tag-message").
   143  		// App should be auto-synced once created
   144  		CreateFromFile(func(app *Application) {
   145  			app.Spec.Source.TargetRevision = "annotated-tag"
   146  			app.Spec.SyncPolicy = &SyncPolicy{Automated: &SyncPolicyAutomated{Prune: true, SelfHeal: false}}
   147  		}).
   148  		Then().
   149  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   150  		ExpectConsistently(SyncStatusIs(SyncStatusCodeSynced), WaitDuration, time.Second*10).
   151  		When().
   152  		// Update the annotated tag to a new git commit, that has a new revisionHistoryLimit.
   153  		PatchFile("guestbook-ui-deployment.yaml", `[{"op": "replace", "path": "/spec/revisionHistoryLimit", "value": 10}]`).
   154  		AddAnnotatedTag("annotated-tag", "my-generic-tag-message").
   155  		Refresh(RefreshTypeHard).
   156  		// The Application should update to the new annotated tag value within 10 seconds.
   157  		And(func() {
   158  			// Deployment revisionHistoryLimit should switch to 10
   159  			timeoutErr := wait.PollUntilContextTimeout(t.Context(), 1*time.Second, 10*time.Second, true, func(context.Context) (done bool, err error) {
   160  				deployment, err := fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Get(t.Context(), "guestbook-ui", metav1.GetOptions{})
   161  				if err != nil {
   162  					return false, nil
   163  				}
   164  
   165  				revisionHistoryLimit := deployment.Spec.RevisionHistoryLimit
   166  				return revisionHistoryLimit != nil && *revisionHistoryLimit == 10, nil
   167  			})
   168  			require.NoError(t, timeoutErr)
   169  		}).
   170  		// Update the Deployment to a different revisionHistoryLimit
   171  		And(func() {
   172  			errors.NewHandler(t).FailOnErr(fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Patch(t.Context(),
   173  				"guestbook-ui", types.MergePatchType, []byte(`{"spec": {"revisionHistoryLimit": 9}}`), metav1.PatchOptions{}))
   174  		}).
   175  		// The revisionHistoryLimit should NOT be self-healed, because selfHealing: false. It should remain at 9.
   176  		And(func() {
   177  			// Wait up to 10 seconds to ensure that deployment revisionHistoryLimit does NOT should switch to 10, it should remain at 9.
   178  			waitErr := wait.PollUntilContextTimeout(t.Context(), 1*time.Second, 10*time.Second, true, func(context.Context) (done bool, err error) {
   179  				deployment, err := fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Get(t.Context(), "guestbook-ui", metav1.GetOptions{})
   180  				if err != nil {
   181  					return false, nil
   182  				}
   183  
   184  				revisionHistoryLimit := deployment.Spec.RevisionHistoryLimit
   185  				return revisionHistoryLimit != nil && *revisionHistoryLimit != 9, nil
   186  			})
   187  			require.Error(t, waitErr, "A timeout error should occur, indicating that revisionHistoryLimit never changed from 9")
   188  		})
   189  }
   190  
   191  func TestAutomatedSelfHealingAgainstLightweightTag(t *testing.T) {
   192  	Given(t).
   193  		Path(guestbookPath).
   194  		When().
   195  		AddTag("annotated-tag").
   196  		// App should be auto-synced once created
   197  		CreateFromFile(func(app *Application) {
   198  			app.Spec.Source.TargetRevision = "annotated-tag"
   199  			app.Spec.SyncPolicy = &SyncPolicy{Automated: &SyncPolicyAutomated{Prune: true, SelfHeal: false}}
   200  		}).
   201  		Then().
   202  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   203  		ExpectConsistently(SyncStatusIs(SyncStatusCodeSynced), WaitDuration, time.Second*10).
   204  		When().
   205  		// Update the annotated tag to a new git commit, that has a new revisionHistoryLimit.
   206  		PatchFile("guestbook-ui-deployment.yaml", `[{"op": "replace", "path": "/spec/revisionHistoryLimit", "value": 10}]`).
   207  		AddTagWithForce("annotated-tag").
   208  		Refresh(RefreshTypeHard).
   209  		// The Application should update to the new annotated tag value within 10 seconds.
   210  		And(func() {
   211  			// Deployment revisionHistoryLimit should switch to 10
   212  			timeoutErr := wait.PollUntilContextTimeout(t.Context(), 1*time.Second, 10*time.Second, true, func(context.Context) (done bool, err error) {
   213  				deployment, err := fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Get(t.Context(), "guestbook-ui", metav1.GetOptions{})
   214  				if err != nil {
   215  					return false, nil
   216  				}
   217  
   218  				revisionHistoryLimit := deployment.Spec.RevisionHistoryLimit
   219  				return revisionHistoryLimit != nil && *revisionHistoryLimit == 10, nil
   220  			})
   221  			require.NoError(t, timeoutErr)
   222  		}).
   223  		// Update the Deployment to a different revisionHistoryLimit
   224  		And(func() {
   225  			errors.NewHandler(t).FailOnErr(fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Patch(t.Context(),
   226  				"guestbook-ui", types.MergePatchType, []byte(`{"spec": {"revisionHistoryLimit": 9}}`), metav1.PatchOptions{}))
   227  		}).
   228  		// The revisionHistoryLimit should NOT be self-healed, because selfHealing: false
   229  		And(func() {
   230  			// Wait up to 10 seconds to ensure that deployment revisionHistoryLimit does NOT should switch to 10, it should remain at 9.
   231  			waitErr := wait.PollUntilContextTimeout(t.Context(), 1*time.Second, 10*time.Second, true, func(context.Context) (done bool, err error) {
   232  				deployment, err := fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Get(t.Context(), "guestbook-ui", metav1.GetOptions{})
   233  				if err != nil {
   234  					return false, nil
   235  				}
   236  
   237  				revisionHistoryLimit := deployment.Spec.RevisionHistoryLimit
   238  				return revisionHistoryLimit != nil && *revisionHistoryLimit != 9, nil
   239  			})
   240  			require.Error(t, waitErr, "A timeout error should occur, indicating that revisionHistoryLimit never changed from 9")
   241  		})
   242  }