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 }