github.com/argoproj/argo-cd/v3@v3.2.1/test/e2e/applicationset_test.go (about) 1 package e2e 2 3 import ( 4 "fmt" 5 "io" 6 "net" 7 "net/http" 8 "net/http/httptest" 9 "testing" 10 11 "github.com/google/uuid" 12 13 corev1 "k8s.io/api/core/v1" 14 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 15 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 16 "k8s.io/apimachinery/pkg/runtime" 17 18 "github.com/argoproj/argo-cd/v3/common" 19 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 20 "github.com/argoproj/argo-cd/v3/test/e2e/fixture" 21 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 25 . "github.com/argoproj/argo-cd/v3/test/e2e/fixture/applicationsets" 26 "github.com/argoproj/argo-cd/v3/test/e2e/fixture/applicationsets/utils" 27 28 "github.com/argoproj/argo-cd/v3/pkg/apis/application" 29 ) 30 31 var ExpectedConditions = []v1alpha1.ApplicationSetCondition{ 32 { 33 Type: v1alpha1.ApplicationSetConditionErrorOccurred, 34 Status: v1alpha1.ApplicationSetConditionStatusFalse, 35 Message: "All applications have been generated successfully", 36 Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate, 37 }, 38 { 39 Type: v1alpha1.ApplicationSetConditionParametersGenerated, 40 Status: v1alpha1.ApplicationSetConditionStatusTrue, 41 Message: "Successfully generated parameters for all Applications", 42 Reason: v1alpha1.ApplicationSetReasonParametersGenerated, 43 }, 44 { 45 Type: v1alpha1.ApplicationSetConditionResourcesUpToDate, 46 Status: v1alpha1.ApplicationSetConditionStatusTrue, 47 Message: "All applications have been generated successfully", 48 Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate, 49 }, 50 } 51 52 func TestSimpleListGeneratorExternalNamespace(t *testing.T) { 53 externalNamespace := string(utils.ArgoCDExternalNamespace) 54 55 expectedApp := v1alpha1.Application{ 56 TypeMeta: metav1.TypeMeta{ 57 Kind: "Application", 58 APIVersion: "argoproj.io/v1alpha1", 59 }, 60 ObjectMeta: metav1.ObjectMeta{ 61 Name: "my-cluster-guestbook", 62 Namespace: externalNamespace, 63 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 64 }, 65 Spec: v1alpha1.ApplicationSpec{ 66 Project: "default", 67 Source: &v1alpha1.ApplicationSource{ 68 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 69 TargetRevision: "HEAD", 70 Path: "guestbook", 71 }, 72 Destination: v1alpha1.ApplicationDestination{ 73 Server: "https://kubernetes.default.svc", 74 Namespace: "guestbook", 75 }, 76 }, 77 } 78 var expectedAppNewNamespace *v1alpha1.Application 79 var expectedAppNewMetadata *v1alpha1.Application 80 81 Given(t). 82 // Create a ListGenerator-based ApplicationSet 83 When(). 84 SwitchToExternalNamespace(utils.ArgoCDExternalNamespace). 85 CreateNamespace(externalNamespace).Create(v1alpha1.ApplicationSet{ 86 ObjectMeta: metav1.ObjectMeta{ 87 Name: "simple-list-generator-external", 88 Namespace: externalNamespace, 89 }, 90 Spec: v1alpha1.ApplicationSetSpec{ 91 GoTemplate: true, 92 Template: v1alpha1.ApplicationSetTemplate{ 93 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"}, 94 Spec: v1alpha1.ApplicationSpec{ 95 Project: "default", 96 Source: &v1alpha1.ApplicationSource{ 97 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 98 TargetRevision: "HEAD", 99 Path: "guestbook", 100 }, 101 Destination: v1alpha1.ApplicationDestination{ 102 Server: "{{.url}}", 103 Namespace: "guestbook", 104 }, 105 }, 106 }, 107 Generators: []v1alpha1.ApplicationSetGenerator{ 108 { 109 List: &v1alpha1.ListGenerator{ 110 Elements: []apiextensionsv1.JSON{{ 111 Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), 112 }}, 113 }, 114 }, 115 }, 116 }, 117 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})). 118 119 // Update the ApplicationSet template namespace, and verify it updates the Applications 120 When(). 121 And(func() { 122 expectedAppNewNamespace = expectedApp.DeepCopy() 123 expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2" 124 }). 125 Update(func(appset *v1alpha1.ApplicationSet) { 126 appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" 127 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})). 128 129 // Update the metadata fields in the appset template, and make sure it propagates to the apps 130 When(). 131 And(func() { 132 expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy() 133 expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} 134 expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{ 135 "label-key": "label-value", 136 } 137 }). 138 Update(func(appset *v1alpha1.ApplicationSet) { 139 appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} 140 appset.Spec.Template.Labels = map[string]string{ 141 "label-key": "label-value", 142 } 143 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewMetadata})). 144 145 // verify the ApplicationSet status conditions were set correctly 146 Expect(ApplicationSetHasConditions("simple-list-generator-external", ExpectedConditions)). 147 148 // Delete the ApplicationSet, and verify it deletes the Applications 149 When(). 150 Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{*expectedAppNewMetadata})) 151 } 152 153 func TestSimpleListGeneratorExternalNamespaceNoConflict(t *testing.T) { 154 externalNamespace := string(utils.ArgoCDExternalNamespace) 155 externalNamespace2 := string(utils.ArgoCDExternalNamespace2) 156 157 expectedApp := v1alpha1.Application{ 158 TypeMeta: metav1.TypeMeta{ 159 Kind: "Application", 160 APIVersion: "argoproj.io/v1alpha1", 161 }, 162 ObjectMeta: metav1.ObjectMeta{ 163 Name: "my-cluster-guestbook", 164 Namespace: externalNamespace, 165 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 166 }, 167 Spec: v1alpha1.ApplicationSpec{ 168 Project: "default", 169 Source: &v1alpha1.ApplicationSource{ 170 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 171 TargetRevision: "HEAD", 172 Path: "guestbook", 173 }, 174 Destination: v1alpha1.ApplicationDestination{ 175 Server: "https://kubernetes.default.svc", 176 Namespace: "guestbook", 177 }, 178 }, 179 } 180 181 expectedAppExternalNamespace2 := v1alpha1.Application{ 182 TypeMeta: metav1.TypeMeta{ 183 Kind: "Application", 184 APIVersion: "argoproj.io/v1alpha1", 185 }, 186 ObjectMeta: metav1.ObjectMeta{ 187 Name: "my-cluster-guestbook", 188 Namespace: externalNamespace2, 189 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 190 }, 191 Spec: v1alpha1.ApplicationSpec{ 192 Project: "default", 193 Source: &v1alpha1.ApplicationSource{ 194 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 195 TargetRevision: "HEAD", 196 Path: "guestbook", 197 }, 198 Destination: v1alpha1.ApplicationDestination{ 199 Server: "https://kubernetes.default.svc", 200 Namespace: "guestbook", 201 }, 202 }, 203 } 204 205 var expectedAppNewNamespace *v1alpha1.Application 206 var expectedAppNewMetadata *v1alpha1.Application 207 208 Given(t). 209 // Create a ListGenerator-based ApplicationSet 210 When(). 211 SwitchToExternalNamespace(utils.ArgoCDExternalNamespace2). 212 CreateNamespace(externalNamespace2).Create(v1alpha1.ApplicationSet{ 213 ObjectMeta: metav1.ObjectMeta{ 214 Name: "simple-list-generator-external", 215 Namespace: externalNamespace2, 216 }, 217 Spec: v1alpha1.ApplicationSetSpec{ 218 GoTemplate: true, 219 Template: v1alpha1.ApplicationSetTemplate{ 220 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"}, 221 Spec: v1alpha1.ApplicationSpec{ 222 Project: "default", 223 Source: &v1alpha1.ApplicationSource{ 224 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 225 TargetRevision: "HEAD", 226 Path: "guestbook", 227 }, 228 Destination: v1alpha1.ApplicationDestination{ 229 Server: "{{.url}}", 230 Namespace: "guestbook", 231 }, 232 }, 233 }, 234 Generators: []v1alpha1.ApplicationSetGenerator{ 235 { 236 List: &v1alpha1.ListGenerator{ 237 Elements: []apiextensionsv1.JSON{{ 238 Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), 239 }}, 240 }, 241 }, 242 }, 243 }, 244 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedAppExternalNamespace2})). 245 When(). 246 SwitchToExternalNamespace(utils.ArgoCDExternalNamespace). 247 CreateNamespace(externalNamespace).Create(v1alpha1.ApplicationSet{ 248 ObjectMeta: metav1.ObjectMeta{ 249 Name: "simple-list-generator-external", 250 Namespace: externalNamespace, 251 }, 252 Spec: v1alpha1.ApplicationSetSpec{ 253 GoTemplate: true, 254 Template: v1alpha1.ApplicationSetTemplate{ 255 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"}, 256 Spec: v1alpha1.ApplicationSpec{ 257 Project: "default", 258 Source: &v1alpha1.ApplicationSource{ 259 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 260 TargetRevision: "HEAD", 261 Path: "guestbook", 262 }, 263 Destination: v1alpha1.ApplicationDestination{ 264 Server: "{{.url}}", 265 Namespace: "guestbook", 266 }, 267 }, 268 }, 269 Generators: []v1alpha1.ApplicationSetGenerator{ 270 { 271 List: &v1alpha1.ListGenerator{ 272 Elements: []apiextensionsv1.JSON{{ 273 Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), 274 }}, 275 }, 276 }, 277 }, 278 }, 279 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})). 280 When(). 281 SwitchToExternalNamespace(utils.ArgoCDExternalNamespace2). 282 Then(). 283 Expect(ApplicationsExist([]v1alpha1.Application{expectedAppExternalNamespace2})). 284 When(). 285 SwitchToExternalNamespace(utils.ArgoCDExternalNamespace). 286 Then(). 287 // Update the ApplicationSet template namespace, and verify it updates the Applications 288 When(). 289 And(func() { 290 expectedAppNewNamespace = expectedApp.DeepCopy() 291 expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2" 292 }). 293 Update(func(appset *v1alpha1.ApplicationSet) { 294 appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" 295 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})). 296 When(). 297 SwitchToExternalNamespace(utils.ArgoCDExternalNamespace2). 298 Then(). 299 Expect(ApplicationsExist([]v1alpha1.Application{expectedAppExternalNamespace2})). 300 When(). 301 SwitchToExternalNamespace(utils.ArgoCDExternalNamespace). 302 Then(). 303 // Update the metadata fields in the appset template, and make sure it propagates to the apps 304 When(). 305 And(func() { 306 expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy() 307 expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} 308 expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{ 309 "label-key": "label-value", 310 } 311 }). 312 Update(func(appset *v1alpha1.ApplicationSet) { 313 appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} 314 appset.Spec.Template.Labels = map[string]string{ 315 "label-key": "label-value", 316 } 317 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewMetadata})). 318 319 // verify the ApplicationSet status conditions were set correctly 320 Expect(ApplicationSetHasConditions("simple-list-generator-external", ExpectedConditions)). 321 When(). 322 SwitchToExternalNamespace(utils.ArgoCDExternalNamespace2). 323 Then(). 324 Expect(ApplicationsExist([]v1alpha1.Application{expectedAppExternalNamespace2})). 325 When(). 326 SwitchToExternalNamespace(utils.ArgoCDExternalNamespace). 327 Then(). 328 // Delete the ApplicationSet, and verify it deletes the Applications 329 When(). 330 Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{*expectedAppNewMetadata})). 331 When(). 332 SwitchToExternalNamespace(utils.ArgoCDExternalNamespace2). 333 Then(). 334 Expect(ApplicationsExist([]v1alpha1.Application{expectedAppExternalNamespace2})). 335 When(). 336 Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{expectedAppExternalNamespace2})) 337 } 338 339 func TestSimpleListGenerator(t *testing.T) { 340 expectedApp := v1alpha1.Application{ 341 TypeMeta: metav1.TypeMeta{ 342 Kind: application.ApplicationKind, 343 APIVersion: "argoproj.io/v1alpha1", 344 }, 345 ObjectMeta: metav1.ObjectMeta{ 346 Name: "my-cluster-guestbook", 347 Namespace: fixture.TestNamespace(), 348 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 349 }, 350 Spec: v1alpha1.ApplicationSpec{ 351 Project: "default", 352 Source: &v1alpha1.ApplicationSource{ 353 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 354 TargetRevision: "HEAD", 355 Path: "guestbook", 356 }, 357 Destination: v1alpha1.ApplicationDestination{ 358 Server: "https://kubernetes.default.svc", 359 Namespace: "guestbook", 360 }, 361 }, 362 } 363 var expectedAppNewNamespace *v1alpha1.Application 364 var expectedAppNewMetadata *v1alpha1.Application 365 366 Given(t). 367 // Create a ListGenerator-based ApplicationSet 368 When().Create(v1alpha1.ApplicationSet{ 369 ObjectMeta: metav1.ObjectMeta{ 370 Name: "simple-list-generator", 371 }, 372 Spec: v1alpha1.ApplicationSetSpec{ 373 Template: v1alpha1.ApplicationSetTemplate{ 374 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{cluster}}-guestbook"}, 375 Spec: v1alpha1.ApplicationSpec{ 376 Project: "default", 377 Source: &v1alpha1.ApplicationSource{ 378 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 379 TargetRevision: "HEAD", 380 Path: "guestbook", 381 }, 382 Destination: v1alpha1.ApplicationDestination{ 383 Server: "{{url}}", 384 Namespace: "guestbook", 385 }, 386 }, 387 }, 388 Generators: []v1alpha1.ApplicationSetGenerator{ 389 { 390 List: &v1alpha1.ListGenerator{ 391 Elements: []apiextensionsv1.JSON{{ 392 Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), 393 }}, 394 }, 395 }, 396 }, 397 }, 398 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})). 399 400 // Update the ApplicationSet template namespace, and verify it updates the Applications 401 When(). 402 And(func() { 403 expectedAppNewNamespace = expectedApp.DeepCopy() 404 expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2" 405 }). 406 Update(func(appset *v1alpha1.ApplicationSet) { 407 appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" 408 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})). 409 410 // Update the metadata fields in the appset template, and make sure it propagates to the apps 411 When(). 412 And(func() { 413 expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy() 414 expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} 415 expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"} 416 }). 417 Update(func(appset *v1alpha1.ApplicationSet) { 418 appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} 419 appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"} 420 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewMetadata})). 421 422 // verify the ApplicationSet status conditions were set correctly 423 Expect(ApplicationSetHasConditions("simple-list-generator", ExpectedConditions)). 424 425 // Delete the ApplicationSet, and verify it deletes the Applications 426 When(). 427 Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{*expectedAppNewMetadata})) 428 } 429 430 func TestSimpleListGeneratorGoTemplate(t *testing.T) { 431 expectedApp := v1alpha1.Application{ 432 TypeMeta: metav1.TypeMeta{ 433 Kind: application.ApplicationKind, 434 APIVersion: "argoproj.io/v1alpha1", 435 }, 436 ObjectMeta: metav1.ObjectMeta{ 437 Name: "my-cluster-guestbook", 438 Namespace: fixture.TestNamespace(), 439 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 440 }, 441 Spec: v1alpha1.ApplicationSpec{ 442 Project: "default", 443 Source: &v1alpha1.ApplicationSource{ 444 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 445 TargetRevision: "HEAD", 446 Path: "guestbook", 447 }, 448 Destination: v1alpha1.ApplicationDestination{ 449 Server: "https://kubernetes.default.svc", 450 Namespace: "guestbook", 451 }, 452 }, 453 } 454 var expectedAppNewNamespace *v1alpha1.Application 455 var expectedAppNewMetadata *v1alpha1.Application 456 457 Given(t). 458 // Create a ListGenerator-based ApplicationSet 459 When().Create(v1alpha1.ApplicationSet{ 460 ObjectMeta: metav1.ObjectMeta{ 461 Name: "simple-list-generator", 462 }, 463 Spec: v1alpha1.ApplicationSetSpec{ 464 GoTemplate: true, 465 Template: v1alpha1.ApplicationSetTemplate{ 466 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"}, 467 Spec: v1alpha1.ApplicationSpec{ 468 Project: "default", 469 Source: &v1alpha1.ApplicationSource{ 470 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 471 TargetRevision: "HEAD", 472 Path: "guestbook", 473 }, 474 Destination: v1alpha1.ApplicationDestination{ 475 Server: "{{.url}}", 476 Namespace: "guestbook", 477 }, 478 }, 479 }, 480 Generators: []v1alpha1.ApplicationSetGenerator{ 481 { 482 List: &v1alpha1.ListGenerator{ 483 Elements: []apiextensionsv1.JSON{{ 484 Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), 485 }}, 486 }, 487 }, 488 }, 489 }, 490 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})). 491 492 // Update the ApplicationSet template namespace, and verify it updates the Applications 493 When(). 494 And(func() { 495 expectedAppNewNamespace = expectedApp.DeepCopy() 496 expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2" 497 }). 498 Update(func(appset *v1alpha1.ApplicationSet) { 499 appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" 500 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})). 501 502 // Update the metadata fields in the appset template, and make sure it propagates to the apps 503 When(). 504 And(func() { 505 expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy() 506 expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} 507 expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"} 508 }). 509 Update(func(appset *v1alpha1.ApplicationSet) { 510 appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} 511 appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"} 512 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewMetadata})). 513 514 // verify the ApplicationSet status conditions were set correctly 515 Expect(ApplicationSetHasConditions("simple-list-generator", ExpectedConditions)). 516 517 // Delete the ApplicationSet, and verify it deletes the Applications 518 When(). 519 Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{*expectedAppNewMetadata})) 520 } 521 522 func TestRenderHelmValuesObject(t *testing.T) { 523 expectedApp := v1alpha1.Application{ 524 TypeMeta: metav1.TypeMeta{ 525 Kind: application.ApplicationKind, 526 APIVersion: "argoproj.io/v1alpha1", 527 }, 528 ObjectMeta: metav1.ObjectMeta{ 529 Name: "my-cluster-guestbook", 530 Namespace: fixture.TestNamespace(), 531 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 532 }, 533 Spec: v1alpha1.ApplicationSpec{ 534 Project: "default", 535 Source: &v1alpha1.ApplicationSource{ 536 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 537 TargetRevision: "HEAD", 538 Path: "helm-guestbook", 539 Helm: &v1alpha1.ApplicationSourceHelm{ 540 ValuesObject: &runtime.RawExtension{ 541 // This will always be converted as yaml 542 Raw: []byte(`{"some":{"string":"Hello world"}}`), 543 }, 544 }, 545 }, 546 Destination: v1alpha1.ApplicationDestination{ 547 Server: "https://kubernetes.default.svc", 548 Namespace: "guestbook", 549 }, 550 }, 551 } 552 553 Given(t). 554 // Create a ListGenerator-based ApplicationSet 555 When().Create(v1alpha1.ApplicationSet{ 556 ObjectMeta: metav1.ObjectMeta{ 557 Name: "test-values-object", 558 }, 559 Spec: v1alpha1.ApplicationSetSpec{ 560 GoTemplate: true, 561 Template: v1alpha1.ApplicationSetTemplate{ 562 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"}, 563 Spec: v1alpha1.ApplicationSpec{ 564 Project: "default", 565 Source: &v1alpha1.ApplicationSource{ 566 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 567 TargetRevision: "HEAD", 568 Path: "helm-guestbook", 569 Helm: &v1alpha1.ApplicationSourceHelm{ 570 ValuesObject: &runtime.RawExtension{ 571 Raw: []byte(`{"some":{"string":"{{.test}}"}}`), 572 }, 573 }, 574 }, 575 Destination: v1alpha1.ApplicationDestination{ 576 Server: "{{.url}}", 577 Namespace: "guestbook", 578 }, 579 }, 580 }, 581 Generators: []v1alpha1.ApplicationSetGenerator{ 582 { 583 List: &v1alpha1.ListGenerator{ 584 Elements: []apiextensionsv1.JSON{{ 585 Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc", "test": "Hello world"}`), 586 }}, 587 }, 588 }, 589 }, 590 }, 591 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})). 592 // Delete the ApplicationSet, and verify it deletes the Applications 593 When(). 594 Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{expectedApp})) 595 } 596 597 func TestTemplatePatch(t *testing.T) { 598 expectedApp := v1alpha1.Application{ 599 TypeMeta: metav1.TypeMeta{ 600 Kind: application.ApplicationKind, 601 APIVersion: "argoproj.io/v1alpha1", 602 }, 603 ObjectMeta: metav1.ObjectMeta{ 604 Name: "my-cluster-guestbook", 605 Namespace: fixture.TestNamespace(), 606 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 607 Annotations: map[string]string{ 608 "annotation-some-key": "annotation-some-value", 609 }, 610 }, 611 Spec: v1alpha1.ApplicationSpec{ 612 Project: "default", 613 Source: &v1alpha1.ApplicationSource{ 614 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 615 TargetRevision: "HEAD", 616 Path: "guestbook", 617 }, 618 Destination: v1alpha1.ApplicationDestination{ 619 Server: "https://kubernetes.default.svc", 620 Namespace: "guestbook", 621 }, 622 SyncPolicy: &v1alpha1.SyncPolicy{ 623 SyncOptions: v1alpha1.SyncOptions{"CreateNamespace=true"}, 624 }, 625 }, 626 } 627 628 templatePatch := `{ 629 "metadata": { 630 "annotations": { 631 {{- range $k, $v := .annotations }} 632 "{{ $k }}": "{{ $v }}" 633 {{- end }} 634 } 635 }, 636 {{- if .createNamespace }} 637 "spec": { 638 "syncPolicy": { 639 "syncOptions": [ 640 "CreateNamespace=true" 641 ] 642 } 643 } 644 {{- end }} 645 } 646 ` 647 648 var expectedAppNewNamespace *v1alpha1.Application 649 var expectedAppNewMetadata *v1alpha1.Application 650 651 Given(t). 652 // Create a ListGenerator-based ApplicationSet 653 When().Create(v1alpha1.ApplicationSet{ 654 ObjectMeta: metav1.ObjectMeta{ 655 Name: "patch-template", 656 }, 657 Spec: v1alpha1.ApplicationSetSpec{ 658 GoTemplate: true, 659 Template: v1alpha1.ApplicationSetTemplate{ 660 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"}, 661 Spec: v1alpha1.ApplicationSpec{ 662 Project: "default", 663 Source: &v1alpha1.ApplicationSource{ 664 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 665 TargetRevision: "HEAD", 666 Path: "guestbook", 667 }, 668 Destination: v1alpha1.ApplicationDestination{ 669 Server: "{{.url}}", 670 Namespace: "guestbook", 671 }, 672 }, 673 }, 674 TemplatePatch: &templatePatch, 675 Generators: []v1alpha1.ApplicationSetGenerator{ 676 { 677 List: &v1alpha1.ListGenerator{ 678 Elements: []apiextensionsv1.JSON{{ 679 Raw: []byte(`{ 680 "cluster": "my-cluster", 681 "url": "https://kubernetes.default.svc", 682 "createNamespace": true, 683 "annotations": { 684 "annotation-some-key": "annotation-some-value" 685 } 686 }`), 687 }}, 688 }, 689 }, 690 }, 691 }, 692 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})). 693 694 // Update the ApplicationSet template namespace, and verify it updates the Applications 695 When(). 696 And(func() { 697 expectedAppNewNamespace = expectedApp.DeepCopy() 698 expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2" 699 }). 700 Update(func(appset *v1alpha1.ApplicationSet) { 701 appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" 702 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})). 703 704 // Update the metadata fields in the appset template, and make sure it propagates to the apps 705 When(). 706 And(func() { 707 expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy() 708 expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{ 709 "label-key": "label-value", 710 } 711 }). 712 Update(func(appset *v1alpha1.ApplicationSet) { 713 appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"} 714 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewMetadata})). 715 716 // verify the ApplicationSet status conditions were set correctly 717 Expect(ApplicationSetHasConditions("patch-template", ExpectedConditions)). 718 719 // Delete the ApplicationSet, and verify it deletes the Applications 720 When(). 721 Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{*expectedAppNewMetadata})) 722 } 723 724 func TestUpdateHelmValuesObject(t *testing.T) { 725 expectedApp := v1alpha1.Application{ 726 TypeMeta: metav1.TypeMeta{ 727 Kind: application.ApplicationKind, 728 APIVersion: "argoproj.io/v1alpha1", 729 }, 730 ObjectMeta: metav1.ObjectMeta{ 731 Name: "my-cluster-guestbook", 732 Namespace: fixture.TestNamespace(), 733 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 734 }, 735 Spec: v1alpha1.ApplicationSpec{ 736 Project: "default", 737 Source: &v1alpha1.ApplicationSource{ 738 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 739 TargetRevision: "HEAD", 740 Path: "helm-guestbook", 741 Helm: &v1alpha1.ApplicationSourceHelm{ 742 ValuesObject: &runtime.RawExtension{ 743 // This will always be converted as yaml 744 Raw: []byte(`{"some":{"foo":"bar"}}`), 745 }, 746 }, 747 }, 748 Destination: v1alpha1.ApplicationDestination{ 749 Server: "https://kubernetes.default.svc", 750 Namespace: "guestbook", 751 }, 752 }, 753 } 754 755 Given(t). 756 // Create a ListGenerator-based ApplicationSet 757 When().Create(v1alpha1.ApplicationSet{ 758 ObjectMeta: metav1.ObjectMeta{ 759 Name: "test-values-object-patch", 760 }, 761 Spec: v1alpha1.ApplicationSetSpec{ 762 GoTemplate: true, 763 Template: v1alpha1.ApplicationSetTemplate{ 764 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"}, 765 Spec: v1alpha1.ApplicationSpec{ 766 Project: "default", 767 Source: &v1alpha1.ApplicationSource{ 768 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 769 TargetRevision: "HEAD", 770 Path: "helm-guestbook", 771 Helm: &v1alpha1.ApplicationSourceHelm{ 772 ValuesObject: &runtime.RawExtension{ 773 Raw: []byte(`{"some":{"string":"{{.test}}"}}`), 774 }, 775 }, 776 }, 777 Destination: v1alpha1.ApplicationDestination{ 778 Server: "{{.url}}", 779 Namespace: "guestbook", 780 }, 781 }, 782 }, 783 Generators: []v1alpha1.ApplicationSetGenerator{ 784 { 785 List: &v1alpha1.ListGenerator{ 786 Elements: []apiextensionsv1.JSON{{ 787 Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc", "test": "Hello world"}`), 788 }}, 789 }, 790 }, 791 }, 792 }, 793 }).Then(). 794 Expect(ApplicationSetHasConditions("test-values-object-patch", ExpectedConditions)). 795 When(). 796 // Update the app spec with some knew ValuesObject to force a merge 797 Update(func(as *v1alpha1.ApplicationSet) { 798 as.Spec.Template.Spec.Source.Helm.ValuesObject = &runtime.RawExtension{ 799 Raw: []byte(`{"some":{"foo":"bar"}}`), 800 } 801 }). 802 Then(). 803 Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})). 804 When(). 805 // Delete the ApplicationSet, and verify it deletes the Applications 806 Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{expectedApp})) 807 } 808 809 func TestSyncPolicyCreateUpdate(t *testing.T) { 810 expectedApp := v1alpha1.Application{ 811 TypeMeta: metav1.TypeMeta{ 812 Kind: "Application", 813 APIVersion: "argoproj.io/v1alpha1", 814 }, 815 ObjectMeta: metav1.ObjectMeta{ 816 Name: "my-cluster-guestbook-sync-policy-create-update", 817 Namespace: utils.ArgoCDNamespace, 818 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 819 }, 820 Spec: v1alpha1.ApplicationSpec{ 821 Project: "default", 822 Source: &v1alpha1.ApplicationSource{ 823 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 824 TargetRevision: "HEAD", 825 Path: "guestbook", 826 }, 827 Destination: v1alpha1.ApplicationDestination{ 828 Server: "https://kubernetes.default.svc", 829 Namespace: "guestbook", 830 }, 831 }, 832 } 833 var expectedAppNewNamespace *v1alpha1.Application 834 var expectedAppNewMetadata *v1alpha1.Application 835 836 Given(t). 837 // Create a ListGenerator-based ApplicationSet 838 When().Create(v1alpha1.ApplicationSet{ 839 ObjectMeta: metav1.ObjectMeta{ 840 Name: "sync-policy-create-update", 841 }, 842 Spec: v1alpha1.ApplicationSetSpec{ 843 GoTemplate: true, 844 Template: v1alpha1.ApplicationSetTemplate{ 845 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ 846 Name: "{{.cluster}}-guestbook-sync-policy-create-update", 847 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 848 }, 849 Spec: v1alpha1.ApplicationSpec{ 850 Project: "default", 851 Source: &v1alpha1.ApplicationSource{ 852 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 853 TargetRevision: "HEAD", 854 Path: "guestbook", 855 }, 856 Destination: v1alpha1.ApplicationDestination{ 857 Server: "{{.url}}", 858 Namespace: "guestbook", 859 }, 860 }, 861 }, 862 Generators: []v1alpha1.ApplicationSetGenerator{ 863 { 864 List: &v1alpha1.ListGenerator{ 865 Elements: []apiextensionsv1.JSON{{ 866 Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), 867 }}, 868 }, 869 }, 870 }, 871 }, 872 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})). 873 874 // Update the ApplicationSet template namespace, and verify it updates the Applications 875 When(). 876 And(func() { 877 expectedAppNewNamespace = expectedApp.DeepCopy() 878 expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2" 879 }). 880 Update(func(appset *v1alpha1.ApplicationSet) { 881 appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" 882 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})). 883 884 // Update the metadata fields in the appset template 885 // Update as well the policy 886 // As policy is create-update, updates must reflected 887 When(). 888 And(func() { 889 expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy() 890 expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} 891 expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{ 892 "label-key": "label-value", 893 } 894 }). 895 Update(func(appset *v1alpha1.ApplicationSet) { 896 appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} 897 appset.Spec.Template.Labels = map[string]string{ 898 "label-key": "label-value", 899 } 900 applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateUpdate 901 appset.Spec.SyncPolicy = &v1alpha1.ApplicationSetSyncPolicy{ 902 ApplicationsSync: &applicationsSyncPolicy, 903 } 904 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewMetadata})). 905 906 // Update the list and remove element 907 // As policy is create-update, app deletion must not be reflected 908 When(). 909 Update(func(appset *v1alpha1.ApplicationSet) { 910 appset.Spec.Generators = []v1alpha1.ApplicationSetGenerator{} 911 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewMetadata})). 912 913 // verify the ApplicationSet status conditions were set correctly 914 Expect(ApplicationSetHasConditions("sync-policy-create-update", ExpectedConditions)). 915 916 // Delete the ApplicationSet, and verify it not deletes the Applications 917 // As policy is create-update, AppSet controller will remove all generated applications's ownerReferences on delete AppSet 918 // So AppSet deletion will be reflected, but all the applications it generates will still exist 919 When(). 920 Delete().Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewMetadata})) 921 } 922 923 func TestSyncPolicyCreateDelete(t *testing.T) { 924 expectedApp := v1alpha1.Application{ 925 TypeMeta: metav1.TypeMeta{ 926 Kind: "Application", 927 APIVersion: "argoproj.io/v1alpha1", 928 }, 929 ObjectMeta: metav1.ObjectMeta{ 930 Name: "my-cluster-guestbook-sync-policy-create-delete", 931 Namespace: utils.ArgoCDNamespace, 932 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 933 }, 934 Spec: v1alpha1.ApplicationSpec{ 935 Project: "default", 936 Source: &v1alpha1.ApplicationSource{ 937 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 938 TargetRevision: "HEAD", 939 Path: "guestbook", 940 }, 941 Destination: v1alpha1.ApplicationDestination{ 942 Server: "https://kubernetes.default.svc", 943 Namespace: "guestbook", 944 }, 945 }, 946 } 947 var expectedAppNewNamespace *v1alpha1.Application 948 949 Given(t). 950 // Create a ListGenerator-based ApplicationSet 951 When().Create(v1alpha1.ApplicationSet{ 952 ObjectMeta: metav1.ObjectMeta{ 953 Name: "sync-policy-create-delete", 954 }, 955 Spec: v1alpha1.ApplicationSetSpec{ 956 GoTemplate: true, 957 Template: v1alpha1.ApplicationSetTemplate{ 958 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook-sync-policy-create-delete"}, 959 Spec: v1alpha1.ApplicationSpec{ 960 Project: "default", 961 Source: &v1alpha1.ApplicationSource{ 962 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 963 TargetRevision: "HEAD", 964 Path: "guestbook", 965 }, 966 Destination: v1alpha1.ApplicationDestination{ 967 Server: "{{.url}}", 968 Namespace: "guestbook", 969 }, 970 }, 971 }, 972 Generators: []v1alpha1.ApplicationSetGenerator{ 973 { 974 List: &v1alpha1.ListGenerator{ 975 Elements: []apiextensionsv1.JSON{{ 976 Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), 977 }}, 978 }, 979 }, 980 }, 981 }, 982 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})). 983 984 // Update the ApplicationSet template namespace, and verify it updates the Applications 985 When(). 986 And(func() { 987 expectedAppNewNamespace = expectedApp.DeepCopy() 988 expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2" 989 }). 990 Update(func(appset *v1alpha1.ApplicationSet) { 991 appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" 992 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})). 993 994 // Update the metadata fields in the appset template 995 // Update as well the policy 996 // As policy is create-delete, updates must not be reflected 997 When(). 998 Update(func(appset *v1alpha1.ApplicationSet) { 999 appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} 1000 appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"} 1001 applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateDelete 1002 appset.Spec.SyncPolicy = &v1alpha1.ApplicationSetSyncPolicy{ 1003 ApplicationsSync: &applicationsSyncPolicy, 1004 } 1005 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})). 1006 1007 // Update the list and remove element 1008 // As policy is create-delete, app deletion must be reflected 1009 When(). 1010 Update(func(appset *v1alpha1.ApplicationSet) { 1011 appset.Spec.Generators = []v1alpha1.ApplicationSetGenerator{} 1012 }).Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{*expectedAppNewNamespace})). 1013 1014 // verify the ApplicationSet status conditions were set correctly 1015 Expect(ApplicationSetHasConditions("sync-policy-create-delete", ExpectedConditions)). 1016 1017 // Delete the ApplicationSet 1018 When(). 1019 Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{*expectedAppNewNamespace})) 1020 } 1021 1022 func TestSyncPolicyCreateOnly(t *testing.T) { 1023 expectedApp := v1alpha1.Application{ 1024 TypeMeta: metav1.TypeMeta{ 1025 Kind: "Application", 1026 APIVersion: "argoproj.io/v1alpha1", 1027 }, 1028 ObjectMeta: metav1.ObjectMeta{ 1029 Name: "my-cluster-guestbook-sync-policy-create-only", 1030 Namespace: utils.ArgoCDNamespace, 1031 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 1032 }, 1033 Spec: v1alpha1.ApplicationSpec{ 1034 Project: "default", 1035 Source: &v1alpha1.ApplicationSource{ 1036 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 1037 TargetRevision: "HEAD", 1038 Path: "guestbook", 1039 }, 1040 Destination: v1alpha1.ApplicationDestination{ 1041 Server: "https://kubernetes.default.svc", 1042 Namespace: "guestbook", 1043 }, 1044 }, 1045 } 1046 var expectedAppNewNamespace *v1alpha1.Application 1047 1048 Given(t). 1049 // Create a ListGenerator-based ApplicationSet 1050 When().Create(v1alpha1.ApplicationSet{ 1051 ObjectMeta: metav1.ObjectMeta{ 1052 Name: "sync-policy-create-only", 1053 }, 1054 Spec: v1alpha1.ApplicationSetSpec{ 1055 GoTemplate: true, 1056 Template: v1alpha1.ApplicationSetTemplate{ 1057 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ 1058 Name: "{{.cluster}}-guestbook-sync-policy-create-only", 1059 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 1060 }, 1061 Spec: v1alpha1.ApplicationSpec{ 1062 Project: "default", 1063 Source: &v1alpha1.ApplicationSource{ 1064 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 1065 TargetRevision: "HEAD", 1066 Path: "guestbook", 1067 }, 1068 Destination: v1alpha1.ApplicationDestination{ 1069 Server: "{{.url}}", 1070 Namespace: "guestbook", 1071 }, 1072 }, 1073 }, 1074 Generators: []v1alpha1.ApplicationSetGenerator{ 1075 { 1076 List: &v1alpha1.ListGenerator{ 1077 Elements: []apiextensionsv1.JSON{{ 1078 Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), 1079 }}, 1080 }, 1081 }, 1082 }, 1083 }, 1084 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})). 1085 1086 // Update the ApplicationSet template namespace, and verify it updates the Applications 1087 When(). 1088 And(func() { 1089 expectedAppNewNamespace = expectedApp.DeepCopy() 1090 expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2" 1091 }). 1092 Update(func(appset *v1alpha1.ApplicationSet) { 1093 appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" 1094 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})). 1095 1096 // Update the metadata fields in the appset template 1097 // Update as well the policy 1098 // As policy is create-only, updates must not be reflected 1099 When(). 1100 Update(func(appset *v1alpha1.ApplicationSet) { 1101 appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} 1102 appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"} 1103 applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateOnly 1104 appset.Spec.SyncPolicy = &v1alpha1.ApplicationSetSyncPolicy{ 1105 ApplicationsSync: &applicationsSyncPolicy, 1106 } 1107 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})). 1108 1109 // Update the list and remove element 1110 // As policy is create-only, app deletion must not be reflected 1111 When(). 1112 Update(func(appset *v1alpha1.ApplicationSet) { 1113 appset.Spec.Generators = []v1alpha1.ApplicationSetGenerator{} 1114 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})). 1115 1116 // verify the ApplicationSet status conditions were set correctly 1117 Expect(ApplicationSetHasConditions("sync-policy-create-only", ExpectedConditions)). 1118 1119 // Delete the ApplicationSet, and verify it not deletes the Applications 1120 // As policy is create-update, AppSet controller will remove all generated applications's ownerReferences on delete AppSet 1121 // So AppSet deletion will be reflected, but all the applications it generates will still exist 1122 When(). 1123 Delete().Then().Expect(ApplicationsExist([]v1alpha1.Application{*expectedAppNewNamespace})) 1124 } 1125 1126 func githubSCMMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { 1127 t.Helper() 1128 return func(w http.ResponseWriter, r *http.Request) { 1129 w.Header().Set("Content-Type", "application/json") 1130 switch r.RequestURI { 1131 case "/api/v3/orgs/argoproj/repos?per_page=100": 1132 _, err := io.WriteString(w, `[ 1133 { 1134 "id": 1296269, 1135 "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", 1136 "name": "argo-cd", 1137 "full_name": "argoproj/argo-cd", 1138 "owner": { 1139 "login": "argoproj", 1140 "id": 1, 1141 "node_id": "MDQ6VXNlcjE=", 1142 "avatar_url": "https://github.com/images/error/argoproj_happy.gif", 1143 "gravatar_id": "", 1144 "url": "https://api.github.com/users/argoproj", 1145 "html_url": "https://github.com/argoproj", 1146 "followers_url": "https://api.github.com/users/argoproj/followers", 1147 "following_url": "https://api.github.com/users/argoproj/following{/other_user}", 1148 "gists_url": "https://api.github.com/users/argoproj/gists{/gist_id}", 1149 "starred_url": "https://api.github.com/users/argoproj/starred{/owner}{/repo}", 1150 "subscriptions_url": "https://api.github.com/users/argoproj/subscriptions", 1151 "organizations_url": "https://api.github.com/users/argoproj/orgs", 1152 "repos_url": "https://api.github.com/users/argoproj/repos", 1153 "events_url": "https://api.github.com/users/argoproj/events{/privacy}", 1154 "received_events_url": "https://api.github.com/users/argoproj/received_events", 1155 "type": "User", 1156 "site_admin": false 1157 }, 1158 "private": false, 1159 "html_url": "https://github.com/argoproj/argo-cd", 1160 "description": "This your first repo!", 1161 "fork": false, 1162 "url": "https://api.github.com/repos/argoproj/argo-cd", 1163 "archive_url": "https://api.github.com/repos/argoproj/argo-cd/{archive_format}{/ref}", 1164 "assignees_url": "https://api.github.com/repos/argoproj/argo-cd/assignees{/user}", 1165 "blobs_url": "https://api.github.com/repos/argoproj/argo-cd/git/blobs{/sha}", 1166 "branches_url": "https://api.github.com/repos/argoproj/argo-cd/branches{/branch}", 1167 "collaborators_url": "https://api.github.com/repos/argoproj/argo-cd/collaborators{/collaborator}", 1168 "comments_url": "https://api.github.com/repos/argoproj/argo-cd/comments{/number}", 1169 "commits_url": "https://api.github.com/repos/argoproj/argo-cd/commits{/sha}", 1170 "compare_url": "https://api.github.com/repos/argoproj/argo-cd/compare/{base}...{head}", 1171 "contents_url": "https://api.github.com/repos/argoproj/argo-cd/contents/{path}", 1172 "contributors_url": "https://api.github.com/repos/argoproj/argo-cd/contributors", 1173 "deployments_url": "https://api.github.com/repos/argoproj/argo-cd/deployments", 1174 "downloads_url": "https://api.github.com/repos/argoproj/argo-cd/downloads", 1175 "events_url": "https://api.github.com/repos/argoproj/argo-cd/events", 1176 "forks_url": "https://api.github.com/repos/argoproj/argo-cd/forks", 1177 "git_commits_url": "https://api.github.com/repos/argoproj/argo-cd/git/commits{/sha}", 1178 "git_refs_url": "https://api.github.com/repos/argoproj/argo-cd/git/refs{/sha}", 1179 "git_tags_url": "https://api.github.com/repos/argoproj/argo-cd/git/tags{/sha}", 1180 "git_url": "git:github.com/argoproj/argo-cd.git", 1181 "issue_comment_url": "https://api.github.com/repos/argoproj/argo-cd/issues/comments{/number}", 1182 "issue_events_url": "https://api.github.com/repos/argoproj/argo-cd/issues/events{/number}", 1183 "issues_url": "https://api.github.com/repos/argoproj/argo-cd/issues{/number}", 1184 "keys_url": "https://api.github.com/repos/argoproj/argo-cd/keys{/key_id}", 1185 "labels_url": "https://api.github.com/repos/argoproj/argo-cd/labels{/name}", 1186 "languages_url": "https://api.github.com/repos/argoproj/argo-cd/languages", 1187 "merges_url": "https://api.github.com/repos/argoproj/argo-cd/merges", 1188 "milestones_url": "https://api.github.com/repos/argoproj/argo-cd/milestones{/number}", 1189 "notifications_url": "https://api.github.com/repos/argoproj/argo-cd/notifications{?since,all,participating}", 1190 "pulls_url": "https://api.github.com/repos/argoproj/argo-cd/pulls{/number}", 1191 "releases_url": "https://api.github.com/repos/argoproj/argo-cd/releases{/id}", 1192 "ssh_url": "git@github.com:argoproj/argo-cd.git", 1193 "stargazers_url": "https://api.github.com/repos/argoproj/argo-cd/stargazers", 1194 "statuses_url": "https://api.github.com/repos/argoproj/argo-cd/statuses/{sha}", 1195 "subscribers_url": "https://api.github.com/repos/argoproj/argo-cd/subscribers", 1196 "subscription_url": "https://api.github.com/repos/argoproj/argo-cd/subscription", 1197 "tags_url": "https://api.github.com/repos/argoproj/argo-cd/tags", 1198 "teams_url": "https://api.github.com/repos/argoproj/argo-cd/teams", 1199 "trees_url": "https://api.github.com/repos/argoproj/argo-cd/git/trees{/sha}", 1200 "clone_url": "https://github.com/argoproj/argo-cd.git", 1201 "mirror_url": "git:git.example.com/argoproj/argo-cd", 1202 "hooks_url": "https://api.github.com/repos/argoproj/argo-cd/hooks", 1203 "svn_url": "https://svn.github.com/argoproj/argo-cd", 1204 "homepage": "https://github.com", 1205 "language": null, 1206 "forks_count": 9, 1207 "stargazers_count": 80, 1208 "watchers_count": 80, 1209 "size": 108, 1210 "default_branch": "master", 1211 "open_issues_count": 0, 1212 "is_template": false, 1213 "topics": [ 1214 "argoproj", 1215 "atom", 1216 "electron", 1217 "api" 1218 ], 1219 "has_issues": true, 1220 "has_projects": true, 1221 "has_wiki": true, 1222 "has_pages": false, 1223 "has_downloads": true, 1224 "archived": false, 1225 "disabled": false, 1226 "visibility": "public", 1227 "pushed_at": "2011-01-26T19:06:43Z", 1228 "created_at": "2011-01-26T19:01:12Z", 1229 "updated_at": "2011-01-26T19:14:43Z", 1230 "permissions": { 1231 "admin": false, 1232 "push": false, 1233 "pull": true 1234 }, 1235 "template_repository": null 1236 } 1237 ]`) 1238 if err != nil { 1239 t.Fail() 1240 } 1241 case "/api/v3/repos/argoproj/argo-cd/branches?per_page=100": 1242 _, err := io.WriteString(w, `[ 1243 { 1244 "name": "master", 1245 "commit": { 1246 "sha": "c5b97d5ae6c19d5c5df71a34c7fbeeda2479ccbc", 1247 "url": "https://api.github.com/repos/argoproj/argo-cd/commits/c5b97d5ae6c19d5c5df71a34c7fbeeda2479ccbc" 1248 }, 1249 "protected": true, 1250 "protection": { 1251 "required_status_checks": { 1252 "enforcement_level": "non_admins", 1253 "contexts": [ 1254 "ci-test", 1255 "linter" 1256 ] 1257 } 1258 }, 1259 "protection_url": "https://api.github.com/repos/argoproj/hello-world/branches/master/protection" 1260 } 1261 ] 1262 `) 1263 if err != nil { 1264 t.Fail() 1265 } 1266 case "/api/v3/repos/argoproj/argo-cd/contents/pkg?ref=master": 1267 _, err := io.WriteString(w, `{ 1268 "type": "file", 1269 "encoding": "base64", 1270 "size": 5362, 1271 "name": "pkg/", 1272 "path": "pkg/", 1273 "content": "encoded content ...", 1274 "sha": "3d21ec53a331a6f037a91c368710b99387d012c1", 1275 "url": "https://api.github.com/repos/octokit/octokit.rb/contents/README.md", 1276 "git_url": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1", 1277 "html_url": "https://github.com/octokit/octokit.rb/blob/master/README.md", 1278 "download_url": "https://raw.githubusercontent.com/octokit/octokit.rb/master/README.md", 1279 "_links": { 1280 "git": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1", 1281 "self": "https://api.github.com/repos/octokit/octokit.rb/contents/README.md", 1282 "html": "https://github.com/octokit/octokit.rb/blob/master/README.md" 1283 } 1284 }`) 1285 if err != nil { 1286 t.Fail() 1287 } 1288 case "/api/v3/repos/argoproj/argo-cd/branches/master": 1289 _, err := io.WriteString(w, `{ 1290 "name": "master", 1291 "commit": { 1292 "sha": "c5b97d5ae6c19d5c5df71a34c7fbeeda2479ccbc", 1293 "url": "https://api.github.com/repos/octocat/Hello-World/commits/c5b97d5ae6c19d5c5df71a34c7fbeeda2479ccbc" 1294 }, 1295 "protected": true, 1296 "protection": { 1297 "required_status_checks": { 1298 "enforcement_level": "non_admins", 1299 "contexts": [ 1300 "ci-test", 1301 "linter" 1302 ] 1303 } 1304 }, 1305 "protection_url": "https://api.github.com/repos/octocat/hello-world/branches/master/protection" 1306 }`) 1307 if err != nil { 1308 t.Fail() 1309 } 1310 default: 1311 w.WriteHeader(http.StatusNotFound) 1312 } 1313 } 1314 } 1315 1316 func testServerWithPort(t *testing.T, port int, handler http.Handler) *httptest.Server { 1317 t.Helper() 1318 // Use mocked API response to avoid rate-limiting. 1319 l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port)) 1320 require.NoError(t, err, "Unable to start server") 1321 1322 ts := httptest.NewUnstartedServer(handler) 1323 1324 ts.Listener.Close() 1325 ts.Listener = l 1326 1327 return ts 1328 } 1329 1330 func TestSimpleSCMProviderGenerator(t *testing.T) { 1331 ts := testServerWithPort(t, 8341, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1332 githubSCMMockHandler(t)(w, r) 1333 })) 1334 ts.Start() 1335 defer ts.Close() 1336 1337 expectedApp := v1alpha1.Application{ 1338 TypeMeta: metav1.TypeMeta{ 1339 Kind: application.ApplicationKind, 1340 APIVersion: "argoproj.io/v1alpha1", 1341 }, 1342 ObjectMeta: metav1.ObjectMeta{ 1343 Name: "argo-cd-guestbook", 1344 Namespace: fixture.TestNamespace(), 1345 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 1346 }, 1347 Spec: v1alpha1.ApplicationSpec{ 1348 Project: "default", 1349 Source: &v1alpha1.ApplicationSource{ 1350 RepoURL: "git@github.com:argoproj/argo-cd.git", 1351 TargetRevision: "master", 1352 Path: "guestbook", 1353 }, 1354 Destination: v1alpha1.ApplicationDestination{ 1355 Server: "https://kubernetes.default.svc", 1356 Namespace: "guestbook", 1357 }, 1358 }, 1359 } 1360 1361 // Because you can't &"". 1362 repoMatch := "argo-cd" 1363 1364 Given(t). 1365 // Create an SCMProviderGenerator-based ApplicationSet 1366 When().Create(v1alpha1.ApplicationSet{ 1367 ObjectMeta: metav1.ObjectMeta{ 1368 Name: "simple-scm-provider-generator", 1369 }, 1370 Spec: v1alpha1.ApplicationSetSpec{ 1371 Template: v1alpha1.ApplicationSetTemplate{ 1372 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{ repository }}-guestbook"}, 1373 Spec: v1alpha1.ApplicationSpec{ 1374 Project: "default", 1375 Source: &v1alpha1.ApplicationSource{ 1376 RepoURL: "{{ url }}", 1377 TargetRevision: "{{ branch }}", 1378 Path: "guestbook", 1379 }, 1380 Destination: v1alpha1.ApplicationDestination{ 1381 Server: "https://kubernetes.default.svc", 1382 Namespace: "guestbook", 1383 }, 1384 }, 1385 }, 1386 Generators: []v1alpha1.ApplicationSetGenerator{ 1387 { 1388 SCMProvider: &v1alpha1.SCMProviderGenerator{ 1389 Github: &v1alpha1.SCMProviderGeneratorGithub{ 1390 Organization: "argoproj", 1391 API: ts.URL, 1392 }, 1393 Filters: []v1alpha1.SCMProviderGeneratorFilter{ 1394 { 1395 RepositoryMatch: &repoMatch, 1396 }, 1397 }, 1398 }, 1399 }, 1400 }, 1401 }, 1402 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})) 1403 } 1404 1405 func TestSimpleSCMProviderGeneratorGoTemplate(t *testing.T) { 1406 ts := testServerWithPort(t, 8342, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1407 githubSCMMockHandler(t)(w, r) 1408 })) 1409 ts.Start() 1410 defer ts.Close() 1411 1412 expectedApp := v1alpha1.Application{ 1413 TypeMeta: metav1.TypeMeta{ 1414 Kind: application.ApplicationKind, 1415 APIVersion: "argoproj.io/v1alpha1", 1416 }, 1417 ObjectMeta: metav1.ObjectMeta{ 1418 Name: "argo-cd-guestbook", 1419 Namespace: fixture.TestNamespace(), 1420 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 1421 }, 1422 Spec: v1alpha1.ApplicationSpec{ 1423 Project: "default", 1424 Source: &v1alpha1.ApplicationSource{ 1425 RepoURL: "git@github.com:argoproj/argo-cd.git", 1426 TargetRevision: "master", 1427 Path: "guestbook", 1428 }, 1429 Destination: v1alpha1.ApplicationDestination{ 1430 Server: "https://kubernetes.default.svc", 1431 Namespace: "guestbook", 1432 }, 1433 }, 1434 } 1435 1436 // Because you can't &"". 1437 repoMatch := "argo-cd" 1438 1439 Given(t). 1440 // Create an SCMProviderGenerator-based ApplicationSet 1441 When().Create(v1alpha1.ApplicationSet{ 1442 ObjectMeta: metav1.ObjectMeta{ 1443 Name: "simple-scm-provider-generator", 1444 }, 1445 Spec: v1alpha1.ApplicationSetSpec{ 1446 GoTemplate: true, 1447 Template: v1alpha1.ApplicationSetTemplate{ 1448 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{ .repository }}-guestbook"}, 1449 Spec: v1alpha1.ApplicationSpec{ 1450 Project: "default", 1451 Source: &v1alpha1.ApplicationSource{ 1452 RepoURL: "{{ .url }}", 1453 TargetRevision: "{{ .branch }}", 1454 Path: "guestbook", 1455 }, 1456 Destination: v1alpha1.ApplicationDestination{ 1457 Server: "https://kubernetes.default.svc", 1458 Namespace: "guestbook", 1459 }, 1460 }, 1461 }, 1462 Generators: []v1alpha1.ApplicationSetGenerator{ 1463 { 1464 SCMProvider: &v1alpha1.SCMProviderGenerator{ 1465 Github: &v1alpha1.SCMProviderGeneratorGithub{ 1466 Organization: "argoproj", 1467 API: ts.URL, 1468 }, 1469 Filters: []v1alpha1.SCMProviderGeneratorFilter{ 1470 { 1471 RepositoryMatch: &repoMatch, 1472 }, 1473 }, 1474 }, 1475 }, 1476 }, 1477 }, 1478 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})) 1479 } 1480 1481 func TestSCMProviderGeneratorSCMProviderNotAllowed(t *testing.T) { 1482 expectedApp := v1alpha1.Application{ 1483 TypeMeta: metav1.TypeMeta{ 1484 Kind: application.ApplicationKind, 1485 APIVersion: "argoproj.io/v1alpha1", 1486 }, 1487 ObjectMeta: metav1.ObjectMeta{ 1488 Name: "argo-cd-guestbook", 1489 Namespace: fixture.TestNamespace(), 1490 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 1491 }, 1492 Spec: v1alpha1.ApplicationSpec{ 1493 Project: "default", 1494 Source: &v1alpha1.ApplicationSource{ 1495 RepoURL: "git@github.com:argoproj/argo-cd.git", 1496 TargetRevision: "master", 1497 Path: "guestbook", 1498 }, 1499 Destination: v1alpha1.ApplicationDestination{ 1500 Server: "https://kubernetes.default.svc", 1501 Namespace: "guestbook", 1502 }, 1503 }, 1504 } 1505 1506 // Because you can't &"". 1507 repoMatch := "argo-cd" 1508 1509 Given(t). 1510 // Create an SCMProviderGenerator-based ApplicationSet 1511 When().Create(v1alpha1.ApplicationSet{ 1512 ObjectMeta: metav1.ObjectMeta{ 1513 Name: "scm-provider-generator-scm-provider-not-allowed", 1514 }, 1515 Spec: v1alpha1.ApplicationSetSpec{ 1516 GoTemplate: true, 1517 Template: v1alpha1.ApplicationSetTemplate{ 1518 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{ .repository }}-guestbook"}, 1519 Spec: v1alpha1.ApplicationSpec{ 1520 Project: "default", 1521 Source: &v1alpha1.ApplicationSource{ 1522 RepoURL: "{{ .url }}", 1523 TargetRevision: "{{ .branch }}", 1524 Path: "guestbook", 1525 }, 1526 Destination: v1alpha1.ApplicationDestination{ 1527 Server: "https://kubernetes.default.svc", 1528 Namespace: "guestbook", 1529 }, 1530 }, 1531 }, 1532 Generators: []v1alpha1.ApplicationSetGenerator{ 1533 { 1534 SCMProvider: &v1alpha1.SCMProviderGenerator{ 1535 Github: &v1alpha1.SCMProviderGeneratorGithub{ 1536 Organization: "argoproj", 1537 API: "http://myservice.mynamespace.svc.cluster.local", 1538 }, 1539 Filters: []v1alpha1.SCMProviderGeneratorFilter{ 1540 { 1541 RepositoryMatch: &repoMatch, 1542 }, 1543 }, 1544 }, 1545 }, 1546 }, 1547 }, 1548 }).Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{expectedApp})). 1549 And(func() { 1550 // app should be listed 1551 output, err := fixture.RunCli("appset", "get", "scm-provider-generator-scm-provider-not-allowed") 1552 require.NoError(t, err) 1553 assert.Contains(t, output, "scm provider not allowed") 1554 }) 1555 } 1556 1557 func TestCustomApplicationFinalizers(t *testing.T) { 1558 expectedApp := v1alpha1.Application{ 1559 TypeMeta: metav1.TypeMeta{ 1560 Kind: application.ApplicationKind, 1561 APIVersion: "argoproj.io/v1alpha1", 1562 }, 1563 ObjectMeta: metav1.ObjectMeta{ 1564 Name: "my-cluster-guestbook", 1565 Namespace: fixture.TestNamespace(), 1566 Finalizers: []string{v1alpha1.BackgroundPropagationPolicyFinalizer}, 1567 }, 1568 Spec: v1alpha1.ApplicationSpec{ 1569 Project: "default", 1570 Source: &v1alpha1.ApplicationSource{ 1571 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 1572 TargetRevision: "HEAD", 1573 Path: "guestbook", 1574 }, 1575 Destination: v1alpha1.ApplicationDestination{ 1576 Server: "https://kubernetes.default.svc", 1577 Namespace: "guestbook", 1578 }, 1579 }, 1580 } 1581 1582 Given(t). 1583 // Create a ListGenerator-based ApplicationSet 1584 When().Create(v1alpha1.ApplicationSet{ 1585 ObjectMeta: metav1.ObjectMeta{ 1586 Name: "simple-list-generator", 1587 }, 1588 Spec: v1alpha1.ApplicationSetSpec{ 1589 Template: v1alpha1.ApplicationSetTemplate{ 1590 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ 1591 Name: "{{cluster}}-guestbook", 1592 Finalizers: []string{v1alpha1.BackgroundPropagationPolicyFinalizer}, 1593 }, 1594 Spec: v1alpha1.ApplicationSpec{ 1595 Project: "default", 1596 Source: &v1alpha1.ApplicationSource{ 1597 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 1598 TargetRevision: "HEAD", 1599 Path: "guestbook", 1600 }, 1601 Destination: v1alpha1.ApplicationDestination{ 1602 Server: "{{url}}", 1603 Namespace: "guestbook", 1604 }, 1605 }, 1606 }, 1607 Generators: []v1alpha1.ApplicationSetGenerator{ 1608 { 1609 List: &v1alpha1.ListGenerator{ 1610 Elements: []apiextensionsv1.JSON{{ 1611 Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), 1612 }}, 1613 }, 1614 }, 1615 }, 1616 }, 1617 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})). 1618 1619 // Delete the ApplicationSet, and verify it deletes the Applications 1620 When(). 1621 Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{expectedApp})) 1622 } 1623 1624 func TestCustomApplicationFinalizersGoTemplate(t *testing.T) { 1625 expectedApp := v1alpha1.Application{ 1626 TypeMeta: metav1.TypeMeta{ 1627 Kind: application.ApplicationKind, 1628 APIVersion: "argoproj.io/v1alpha1", 1629 }, 1630 ObjectMeta: metav1.ObjectMeta{ 1631 Name: "my-cluster-guestbook", 1632 Namespace: fixture.TestNamespace(), 1633 Finalizers: []string{v1alpha1.BackgroundPropagationPolicyFinalizer}, 1634 }, 1635 Spec: v1alpha1.ApplicationSpec{ 1636 Project: "default", 1637 Source: &v1alpha1.ApplicationSource{ 1638 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 1639 TargetRevision: "HEAD", 1640 Path: "guestbook", 1641 }, 1642 Destination: v1alpha1.ApplicationDestination{ 1643 Server: "https://kubernetes.default.svc", 1644 Namespace: "guestbook", 1645 }, 1646 }, 1647 } 1648 1649 Given(t). 1650 // Create a ListGenerator-based ApplicationSet 1651 When().Create(v1alpha1.ApplicationSet{ 1652 ObjectMeta: metav1.ObjectMeta{ 1653 Name: "simple-list-generator", 1654 }, 1655 Spec: v1alpha1.ApplicationSetSpec{ 1656 GoTemplate: true, 1657 Template: v1alpha1.ApplicationSetTemplate{ 1658 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ 1659 Name: "{{.cluster}}-guestbook", 1660 Finalizers: []string{v1alpha1.BackgroundPropagationPolicyFinalizer}, 1661 }, 1662 Spec: v1alpha1.ApplicationSpec{ 1663 Project: "default", 1664 Source: &v1alpha1.ApplicationSource{ 1665 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 1666 TargetRevision: "HEAD", 1667 Path: "guestbook", 1668 }, 1669 Destination: v1alpha1.ApplicationDestination{ 1670 Server: "{{.url}}", 1671 Namespace: "guestbook", 1672 }, 1673 }, 1674 }, 1675 Generators: []v1alpha1.ApplicationSetGenerator{ 1676 { 1677 List: &v1alpha1.ListGenerator{ 1678 Elements: []apiextensionsv1.JSON{{ 1679 Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), 1680 }}, 1681 }, 1682 }, 1683 }, 1684 }, 1685 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})). 1686 1687 // Delete the ApplicationSet, and verify it deletes the Applications 1688 When(). 1689 Delete().Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{expectedApp})) 1690 } 1691 1692 func githubPullMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { 1693 t.Helper() 1694 return func(w http.ResponseWriter, r *http.Request) { 1695 w.Header().Set("Content-Type", "application/json") 1696 switch r.RequestURI { 1697 case "/api/v3/repos/applicationset-test-org/argocd-example-apps/pulls?per_page=100": 1698 _, err := io.WriteString(w, `[ 1699 { 1700 "number": 1, 1701 "title": "title1", 1702 "labels": [ 1703 { 1704 "name": "preview" 1705 } 1706 ], 1707 "base": { 1708 "ref": "master", 1709 "sha": "7a4a5c987fdfb2b0629e9dbf5f31636c69ba4775" 1710 }, 1711 "head": { 1712 "ref": "pull-request", 1713 "sha": "824a5c987fdfb2b0629e9dbf5f31636c69ba4772" 1714 }, 1715 "user": { 1716 "login": "testName" 1717 } 1718 } 1719 ]`) 1720 if err != nil { 1721 t.Fail() 1722 } 1723 default: 1724 w.WriteHeader(http.StatusNotFound) 1725 } 1726 } 1727 } 1728 1729 func TestSimpleSCMProviderGeneratorTokenRefStrictOk(t *testing.T) { 1730 secretName := uuid.New().String() 1731 1732 ts := testServerWithPort(t, 8341, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1733 githubSCMMockHandler(t)(w, r) 1734 })) 1735 1736 ts.Start() 1737 defer ts.Close() 1738 1739 expectedApp := v1alpha1.Application{ 1740 TypeMeta: metav1.TypeMeta{ 1741 Kind: application.ApplicationKind, 1742 APIVersion: "argoproj.io/v1alpha1", 1743 }, 1744 ObjectMeta: metav1.ObjectMeta{ 1745 Name: "argo-cd-guestbook", 1746 Namespace: fixture.TestNamespace(), 1747 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 1748 }, 1749 Spec: v1alpha1.ApplicationSpec{ 1750 Project: "default", 1751 Source: &v1alpha1.ApplicationSource{ 1752 RepoURL: "git@github.com:argoproj/argo-cd.git", 1753 TargetRevision: "master", 1754 Path: "guestbook", 1755 }, 1756 Destination: v1alpha1.ApplicationDestination{ 1757 Server: "https://kubernetes.default.svc", 1758 Namespace: "guestbook", 1759 }, 1760 }, 1761 } 1762 1763 // Because you can't &"". 1764 repoMatch := "argo-cd" 1765 1766 Given(t). 1767 And(func() { 1768 _, err := utils.GetE2EFixtureK8sClient(t).KubeClientset.CoreV1().Secrets(fixture.TestNamespace()).Create(t.Context(), &corev1.Secret{ 1769 ObjectMeta: metav1.ObjectMeta{ 1770 Namespace: fixture.TestNamespace(), 1771 Name: secretName, 1772 Labels: map[string]string{ 1773 common.LabelKeySecretType: common.LabelValueSecretTypeSCMCreds, 1774 }, 1775 }, 1776 Data: map[string][]byte{ 1777 "hello": []byte("world"), 1778 }, 1779 }, metav1.CreateOptions{}) 1780 1781 assert.NoError(t, err) 1782 }). 1783 // Create an SCMProviderGenerator-based ApplicationSet 1784 When().Create(v1alpha1.ApplicationSet{ 1785 ObjectMeta: metav1.ObjectMeta{ 1786 Name: "simple-scm-provider-generator-strict", 1787 }, 1788 Spec: v1alpha1.ApplicationSetSpec{ 1789 Template: v1alpha1.ApplicationSetTemplate{ 1790 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{ repository }}-guestbook"}, 1791 Spec: v1alpha1.ApplicationSpec{ 1792 Project: "default", 1793 Source: &v1alpha1.ApplicationSource{ 1794 RepoURL: "{{ url }}", 1795 TargetRevision: "{{ branch }}", 1796 Path: "guestbook", 1797 }, 1798 Destination: v1alpha1.ApplicationDestination{ 1799 Server: "https://kubernetes.default.svc", 1800 Namespace: "guestbook", 1801 }, 1802 }, 1803 }, 1804 Generators: []v1alpha1.ApplicationSetGenerator{ 1805 { 1806 SCMProvider: &v1alpha1.SCMProviderGenerator{ 1807 Github: &v1alpha1.SCMProviderGeneratorGithub{ 1808 Organization: "argoproj", 1809 API: ts.URL, 1810 TokenRef: &v1alpha1.SecretRef{ 1811 SecretName: secretName, 1812 Key: "hello", 1813 }, 1814 }, 1815 Filters: []v1alpha1.SCMProviderGeneratorFilter{ 1816 { 1817 RepositoryMatch: &repoMatch, 1818 }, 1819 }, 1820 }, 1821 }, 1822 }, 1823 }, 1824 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})). 1825 When().And(func() { 1826 err := utils.GetE2EFixtureK8sClient(t).KubeClientset.CoreV1().Secrets(fixture.TestNamespace()).Delete(t.Context(), secretName, metav1.DeleteOptions{}) 1827 assert.NoError(t, err) 1828 }) 1829 } 1830 1831 func TestSimpleSCMProviderGeneratorTokenRefStrictKo(t *testing.T) { 1832 secretName := uuid.New().String() 1833 1834 ts := testServerWithPort(t, 8341, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1835 githubSCMMockHandler(t)(w, r) 1836 })) 1837 1838 ts.Start() 1839 defer ts.Close() 1840 1841 expectedApp := v1alpha1.Application{ 1842 TypeMeta: metav1.TypeMeta{ 1843 Kind: application.ApplicationKind, 1844 APIVersion: "argoproj.io/v1alpha1", 1845 }, 1846 ObjectMeta: metav1.ObjectMeta{ 1847 Name: "argo-cd-guestbook", 1848 Namespace: fixture.TestNamespace(), 1849 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 1850 Labels: map[string]string{ 1851 common.LabelKeyAppInstance: "simple-scm-provider-generator-strict-ko", 1852 }, 1853 }, 1854 Spec: v1alpha1.ApplicationSpec{ 1855 Project: "default", 1856 Source: &v1alpha1.ApplicationSource{ 1857 RepoURL: "git@github.com:argoproj/argo-cd.git", 1858 TargetRevision: "master", 1859 Path: "guestbook", 1860 }, 1861 Destination: v1alpha1.ApplicationDestination{ 1862 Server: "https://kubernetes.default.svc", 1863 Namespace: "guestbook", 1864 }, 1865 }, 1866 } 1867 1868 // Because you can't &"". 1869 repoMatch := "argo-cd" 1870 1871 Given(t). 1872 And(func() { 1873 _, err := utils.GetE2EFixtureK8sClient(t).KubeClientset.CoreV1().Secrets(fixture.TestNamespace()).Create(t.Context(), &corev1.Secret{ 1874 ObjectMeta: metav1.ObjectMeta{ 1875 Namespace: fixture.TestNamespace(), 1876 Name: secretName, 1877 Labels: map[string]string{ 1878 // Try to exfiltrate cluster secret 1879 common.LabelKeySecretType: common.LabelValueSecretTypeCluster, 1880 }, 1881 }, 1882 Data: map[string][]byte{ 1883 "hello": []byte("world"), 1884 }, 1885 }, metav1.CreateOptions{}) 1886 1887 assert.NoError(t, err) 1888 }). 1889 // Create an SCMProviderGenerator-based ApplicationSet 1890 When().Create(v1alpha1.ApplicationSet{ 1891 ObjectMeta: metav1.ObjectMeta{ 1892 Name: "simple-scm-provider-generator-strict-ko", 1893 }, 1894 Spec: v1alpha1.ApplicationSetSpec{ 1895 Template: v1alpha1.ApplicationSetTemplate{ 1896 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{ repository }}-guestbook"}, 1897 Spec: v1alpha1.ApplicationSpec{ 1898 Project: "default", 1899 Source: &v1alpha1.ApplicationSource{ 1900 RepoURL: "{{ url }}", 1901 TargetRevision: "{{ branch }}", 1902 Path: "guestbook", 1903 }, 1904 Destination: v1alpha1.ApplicationDestination{ 1905 Server: "https://kubernetes.default.svc", 1906 Namespace: "guestbook", 1907 }, 1908 }, 1909 }, 1910 Generators: []v1alpha1.ApplicationSetGenerator{ 1911 { 1912 SCMProvider: &v1alpha1.SCMProviderGenerator{ 1913 Github: &v1alpha1.SCMProviderGeneratorGithub{ 1914 Organization: "argoproj", 1915 API: ts.URL, 1916 TokenRef: &v1alpha1.SecretRef{ 1917 SecretName: secretName, 1918 Key: "hello", 1919 }, 1920 }, 1921 Filters: []v1alpha1.SCMProviderGeneratorFilter{ 1922 { 1923 RepositoryMatch: &repoMatch, 1924 }, 1925 }, 1926 }, 1927 }, 1928 }, 1929 }, 1930 }).Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{expectedApp})). 1931 When(). 1932 And(func() { 1933 // app should be listed 1934 output, err := fixture.RunCli("appset", "get", "simple-scm-provider-generator-strict-ko") 1935 require.NoError(t, err) 1936 assert.Contains(t, output, fmt.Sprintf("scm provider: error fetching Github token: secret %s/%s is not a valid SCM creds secret", fixture.TestNamespace(), secretName)) 1937 err2 := utils.GetE2EFixtureK8sClient(t).KubeClientset.CoreV1().Secrets(fixture.TestNamespace()).Delete(t.Context(), secretName, metav1.DeleteOptions{}) 1938 assert.NoError(t, err2) 1939 }) 1940 } 1941 1942 func TestSimplePullRequestGenerator(t *testing.T) { 1943 ts := testServerWithPort(t, 8343, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1944 githubPullMockHandler(t)(w, r) 1945 })) 1946 1947 ts.Start() 1948 defer ts.Close() 1949 1950 expectedApp := v1alpha1.Application{ 1951 TypeMeta: metav1.TypeMeta{ 1952 Kind: application.ApplicationKind, 1953 APIVersion: "argoproj.io/v1alpha1", 1954 }, 1955 ObjectMeta: metav1.ObjectMeta{ 1956 Name: "guestbook-1", 1957 Namespace: fixture.TestNamespace(), 1958 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 1959 }, 1960 Spec: v1alpha1.ApplicationSpec{ 1961 Project: "default", 1962 Source: &v1alpha1.ApplicationSource{ 1963 RepoURL: "git@github.com:applicationset-test-org/argocd-example-apps.git", 1964 TargetRevision: "824a5c987fdfb2b0629e9dbf5f31636c69ba4772", 1965 Path: "kustomize-guestbook", 1966 Kustomize: &v1alpha1.ApplicationSourceKustomize{ 1967 NamePrefix: "guestbook-1", 1968 }, 1969 }, 1970 Destination: v1alpha1.ApplicationDestination{ 1971 Server: "https://kubernetes.default.svc", 1972 Namespace: "guestbook-pull-request", 1973 }, 1974 }, 1975 } 1976 1977 Given(t). 1978 // Create an PullRequestGenerator-based ApplicationSet 1979 When().Create(v1alpha1.ApplicationSet{ 1980 ObjectMeta: metav1.ObjectMeta{ 1981 Name: "simple-pull-request-generator", 1982 }, 1983 Spec: v1alpha1.ApplicationSetSpec{ 1984 Template: v1alpha1.ApplicationSetTemplate{ 1985 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "guestbook-{{ number }}"}, 1986 Spec: v1alpha1.ApplicationSpec{ 1987 Project: "default", 1988 Source: &v1alpha1.ApplicationSource{ 1989 RepoURL: "git@github.com:applicationset-test-org/argocd-example-apps.git", 1990 TargetRevision: "{{ head_sha }}", 1991 Path: "kustomize-guestbook", 1992 Kustomize: &v1alpha1.ApplicationSourceKustomize{ 1993 NamePrefix: "guestbook-{{ number }}", 1994 }, 1995 }, 1996 Destination: v1alpha1.ApplicationDestination{ 1997 Server: "https://kubernetes.default.svc", 1998 Namespace: "guestbook-{{ branch }}", 1999 }, 2000 }, 2001 }, 2002 Generators: []v1alpha1.ApplicationSetGenerator{ 2003 { 2004 PullRequest: &v1alpha1.PullRequestGenerator{ 2005 Github: &v1alpha1.PullRequestGeneratorGithub{ 2006 API: ts.URL, 2007 Owner: "applicationset-test-org", 2008 Repo: "argocd-example-apps", 2009 Labels: []string{ 2010 "preview", 2011 }, 2012 }, 2013 }, 2014 }, 2015 }, 2016 }, 2017 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})) 2018 } 2019 2020 func TestSimplePullRequestGeneratorGoTemplate(t *testing.T) { 2021 ts := testServerWithPort(t, 8344, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 2022 githubPullMockHandler(t)(w, r) 2023 })) 2024 2025 ts.Start() 2026 defer ts.Close() 2027 2028 expectedApp := v1alpha1.Application{ 2029 TypeMeta: metav1.TypeMeta{ 2030 Kind: application.ApplicationKind, 2031 APIVersion: "argoproj.io/v1alpha1", 2032 }, 2033 ObjectMeta: metav1.ObjectMeta{ 2034 Name: "guestbook-1", 2035 Namespace: fixture.TestNamespace(), 2036 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 2037 Labels: map[string]string{"app": "preview"}, 2038 }, 2039 Spec: v1alpha1.ApplicationSpec{ 2040 Project: "default", 2041 Source: &v1alpha1.ApplicationSource{ 2042 RepoURL: "git@github.com:applicationset-test-org/argocd-example-apps.git", 2043 TargetRevision: "824a5c987fdfb2b0629e9dbf5f31636c69ba4772", 2044 Path: "kustomize-guestbook", 2045 Kustomize: &v1alpha1.ApplicationSourceKustomize{ 2046 NamePrefix: "guestbook-1", 2047 }, 2048 }, 2049 Destination: v1alpha1.ApplicationDestination{ 2050 Server: "https://kubernetes.default.svc", 2051 Namespace: "guestbook-pull-request", 2052 }, 2053 }, 2054 } 2055 2056 Given(t). 2057 // Create an PullRequestGenerator-based ApplicationSet 2058 When().Create(v1alpha1.ApplicationSet{ 2059 ObjectMeta: metav1.ObjectMeta{ 2060 Name: "simple-pull-request-generator", 2061 }, 2062 Spec: v1alpha1.ApplicationSetSpec{ 2063 GoTemplate: true, 2064 Template: v1alpha1.ApplicationSetTemplate{ 2065 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ 2066 Name: "guestbook-{{ .number }}", 2067 Labels: map[string]string{"app": "{{index .labels 0}}"}, 2068 }, 2069 Spec: v1alpha1.ApplicationSpec{ 2070 Project: "default", 2071 Source: &v1alpha1.ApplicationSource{ 2072 RepoURL: "git@github.com:applicationset-test-org/argocd-example-apps.git", 2073 TargetRevision: "{{ .head_sha }}", 2074 Path: "kustomize-guestbook", 2075 Kustomize: &v1alpha1.ApplicationSourceKustomize{ 2076 NamePrefix: "guestbook-{{ .number }}", 2077 }, 2078 }, 2079 Destination: v1alpha1.ApplicationDestination{ 2080 Server: "https://kubernetes.default.svc", 2081 Namespace: "guestbook-{{ .branch }}", 2082 }, 2083 }, 2084 }, 2085 Generators: []v1alpha1.ApplicationSetGenerator{ 2086 { 2087 PullRequest: &v1alpha1.PullRequestGenerator{ 2088 Github: &v1alpha1.PullRequestGeneratorGithub{ 2089 API: ts.URL, 2090 Owner: "applicationset-test-org", 2091 Repo: "argocd-example-apps", 2092 Labels: []string{ 2093 "preview", 2094 }, 2095 }, 2096 }, 2097 }, 2098 }, 2099 }, 2100 }).Then().Expect(ApplicationsExist([]v1alpha1.Application{expectedApp})) 2101 } 2102 2103 func TestPullRequestGeneratorNotAllowedSCMProvider(t *testing.T) { 2104 expectedApp := v1alpha1.Application{ 2105 TypeMeta: metav1.TypeMeta{ 2106 Kind: application.ApplicationKind, 2107 APIVersion: "argoproj.io/v1alpha1", 2108 }, 2109 ObjectMeta: metav1.ObjectMeta{ 2110 Name: "guestbook-1", 2111 Namespace: fixture.TestNamespace(), 2112 Finalizers: []string{v1alpha1.ResourcesFinalizerName}, 2113 Labels: map[string]string{ 2114 "app": "preview", 2115 }, 2116 }, 2117 Spec: v1alpha1.ApplicationSpec{ 2118 Project: "default", 2119 Source: &v1alpha1.ApplicationSource{ 2120 RepoURL: "git@github.com:applicationset-test-org/argocd-example-apps.git", 2121 TargetRevision: "824a5c987fdfb2b0629e9dbf5f31636c69ba4772", 2122 Path: "kustomize-guestbook", 2123 Kustomize: &v1alpha1.ApplicationSourceKustomize{ 2124 NamePrefix: "guestbook-1", 2125 }, 2126 }, 2127 Destination: v1alpha1.ApplicationDestination{ 2128 Server: "https://kubernetes.default.svc", 2129 Namespace: "guestbook-pull-request", 2130 }, 2131 }, 2132 } 2133 2134 Given(t). 2135 // Create an PullRequestGenerator-based ApplicationSet 2136 When().Create(v1alpha1.ApplicationSet{ 2137 ObjectMeta: metav1.ObjectMeta{ 2138 Name: "pull-request-generator-not-allowed-scm", 2139 }, 2140 Spec: v1alpha1.ApplicationSetSpec{ 2141 GoTemplate: true, 2142 Template: v1alpha1.ApplicationSetTemplate{ 2143 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ 2144 Name: "guestbook-{{ .number }}", 2145 Labels: map[string]string{"app": "{{index .labels 0}}"}, 2146 }, 2147 Spec: v1alpha1.ApplicationSpec{ 2148 Project: "default", 2149 Source: &v1alpha1.ApplicationSource{ 2150 RepoURL: "git@github.com:applicationset-test-org/argocd-example-apps.git", 2151 TargetRevision: "{{ .head_sha }}", 2152 Path: "kustomize-guestbook", 2153 Kustomize: &v1alpha1.ApplicationSourceKustomize{ 2154 NamePrefix: "guestbook-{{ .number }}", 2155 }, 2156 }, 2157 Destination: v1alpha1.ApplicationDestination{ 2158 Server: "https://kubernetes.default.svc", 2159 Namespace: "guestbook-{{ .branch }}", 2160 }, 2161 }, 2162 }, 2163 Generators: []v1alpha1.ApplicationSetGenerator{ 2164 { 2165 PullRequest: &v1alpha1.PullRequestGenerator{ 2166 Github: &v1alpha1.PullRequestGeneratorGithub{ 2167 API: "http://myservice.mynamespace.svc.cluster.local", 2168 Owner: "applicationset-test-org", 2169 Repo: "argocd-example-apps", 2170 Labels: []string{ 2171 "preview", 2172 }, 2173 }, 2174 }, 2175 }, 2176 }, 2177 }, 2178 }).Then().Expect(ApplicationsDoNotExist([]v1alpha1.Application{expectedApp})). 2179 And(func() { 2180 // app should be listed 2181 output, err := fixture.RunCli("appset", "get", "pull-request-generator-not-allowed-scm") 2182 require.NoError(t, err) 2183 assert.Contains(t, output, "scm provider not allowed") 2184 }) 2185 }