github.com/argoproj/argo-cd/v2@v2.10.5/test/e2e/matrix_e2e_test.go (about) 1 package e2e 2 3 import ( 4 "fmt" 5 "testing" 6 7 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 8 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 10 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 11 argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 12 . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets" 13 "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils" 14 15 "github.com/argoproj/argo-cd/v2/pkg/apis/application" 16 ) 17 18 func TestListMatrixGenerator(t *testing.T) { 19 generateExpectedApp := func(cluster, name string) argov1alpha1.Application { 20 return argov1alpha1.Application{ 21 TypeMeta: metav1.TypeMeta{ 22 Kind: application.ApplicationKind, 23 APIVersion: "argoproj.io/v1alpha1", 24 }, 25 ObjectMeta: metav1.ObjectMeta{ 26 Name: fmt.Sprintf("%s-%s", cluster, name), 27 Namespace: utils.TestNamespace(), 28 Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, 29 }, 30 Spec: argov1alpha1.ApplicationSpec{ 31 Project: "default", 32 Source: &argov1alpha1.ApplicationSource{ 33 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 34 TargetRevision: "HEAD", 35 Path: name, 36 }, 37 Destination: argov1alpha1.ApplicationDestination{ 38 Server: "https://kubernetes.default.svc", 39 Namespace: name, 40 }, 41 }, 42 } 43 } 44 45 expectedApps := []argov1alpha1.Application{ 46 generateExpectedApp("cluster1", "kustomize-guestbook"), 47 generateExpectedApp("cluster1", "helm-guestbook"), 48 generateExpectedApp("cluster1", "ksonnet-guestbook"), 49 50 generateExpectedApp("cluster2", "kustomize-guestbook"), 51 generateExpectedApp("cluster2", "helm-guestbook"), 52 generateExpectedApp("cluster2", "ksonnet-guestbook"), 53 } 54 55 var expectedAppsNewNamespace []argov1alpha1.Application 56 var expectedAppsNewMetadata []argov1alpha1.Application 57 58 Given(t). 59 // Create a ClusterGenerator-based ApplicationSet 60 When(). 61 Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ 62 Name: "matrix-generator", 63 }, 64 Spec: v1alpha1.ApplicationSetSpec{ 65 Template: v1alpha1.ApplicationSetTemplate{ 66 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{values.name}}-{{path.basename}}"}, 67 Spec: argov1alpha1.ApplicationSpec{ 68 Project: "default", 69 Source: &argov1alpha1.ApplicationSource{ 70 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 71 TargetRevision: "HEAD", 72 Path: "{{path}}", 73 }, 74 Destination: argov1alpha1.ApplicationDestination{ 75 Server: "https://kubernetes.default.svc", 76 Namespace: "{{path.basename}}", 77 }, 78 }, 79 }, 80 Generators: []v1alpha1.ApplicationSetGenerator{ 81 { 82 Matrix: &v1alpha1.MatrixGenerator{ 83 Generators: []v1alpha1.ApplicationSetNestedGenerator{ 84 { 85 List: &v1alpha1.ListGenerator{ 86 Elements: []apiextensionsv1.JSON{ 87 {Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc", "values": {"name": "cluster1"}}`)}, 88 {Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc", "values": {"name": "cluster2"}}`)}, 89 }, 90 }, 91 }, 92 { 93 Git: &v1alpha1.GitGenerator{ 94 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 95 Directories: []v1alpha1.GitDirectoryGeneratorItem{ 96 { 97 Path: "*guestbook*", 98 }, 99 }, 100 }, 101 }, 102 }, 103 }, 104 }, 105 }, 106 }, 107 }).Then().Expect(ApplicationsExist(expectedApps)). 108 109 // Update the ApplicationSet template namespace, and verify it updates the Applications 110 When(). 111 And(func() { 112 for _, expectedApp := range expectedApps { 113 newExpectedApp := expectedApp.DeepCopy() 114 newExpectedApp.Spec.Destination.Namespace = "guestbook2" 115 expectedAppsNewNamespace = append(expectedAppsNewNamespace, *newExpectedApp) 116 } 117 }). 118 Update(func(appset *v1alpha1.ApplicationSet) { 119 appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" 120 }).Then().Expect(ApplicationsExist(expectedAppsNewNamespace)). 121 122 // Update the metadata fields in the appset template, and make sure it propagates to the apps 123 When(). 124 And(func() { 125 for _, expectedApp := range expectedAppsNewNamespace { 126 expectedAppNewMetadata := expectedApp.DeepCopy() 127 expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} 128 expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"} 129 expectedAppsNewMetadata = append(expectedAppsNewMetadata, *expectedAppNewMetadata) 130 } 131 }). 132 Update(func(appset *v1alpha1.ApplicationSet) { 133 appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} 134 appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"} 135 }).Then().Expect(ApplicationsExist(expectedAppsNewMetadata)). 136 137 // Delete the ApplicationSet, and verify it deletes the Applications 138 When(). 139 Delete().Then().Expect(ApplicationsDoNotExist(expectedAppsNewNamespace)) 140 } 141 142 func TestClusterMatrixGenerator(t *testing.T) { 143 generateExpectedApp := func(cluster, name string) argov1alpha1.Application { 144 return argov1alpha1.Application{ 145 TypeMeta: metav1.TypeMeta{ 146 Kind: application.ApplicationKind, 147 APIVersion: "argoproj.io/v1alpha1", 148 }, 149 ObjectMeta: metav1.ObjectMeta{ 150 Name: fmt.Sprintf("%s-%s", cluster, name), 151 Namespace: utils.TestNamespace(), 152 Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, 153 }, 154 Spec: argov1alpha1.ApplicationSpec{ 155 Project: "default", 156 Source: &argov1alpha1.ApplicationSource{ 157 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 158 TargetRevision: "HEAD", 159 Path: name, 160 }, 161 Destination: argov1alpha1.ApplicationDestination{ 162 Name: cluster, 163 Namespace: name, 164 }, 165 }, 166 } 167 } 168 169 expectedApps := []argov1alpha1.Application{ 170 generateExpectedApp("cluster1", "kustomize-guestbook"), 171 generateExpectedApp("cluster1", "helm-guestbook"), 172 generateExpectedApp("cluster1", "ksonnet-guestbook"), 173 174 generateExpectedApp("cluster2", "kustomize-guestbook"), 175 generateExpectedApp("cluster2", "helm-guestbook"), 176 generateExpectedApp("cluster2", "ksonnet-guestbook"), 177 } 178 179 var expectedAppsNewNamespace []argov1alpha1.Application 180 var expectedAppsNewMetadata []argov1alpha1.Application 181 182 Given(t). 183 // Create a ClusterGenerator-based ApplicationSet 184 When(). 185 CreateClusterSecret("my-secret", "cluster1", "https://kubernetes.default.svc"). 186 CreateClusterSecret("my-secret2", "cluster2", "https://kubernetes.default.svc"). 187 Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ 188 Name: "matrix-generator", 189 }, 190 Spec: v1alpha1.ApplicationSetSpec{ 191 Template: v1alpha1.ApplicationSetTemplate{ 192 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{name}}-{{path.basename}}"}, 193 Spec: argov1alpha1.ApplicationSpec{ 194 Project: "default", 195 Source: &argov1alpha1.ApplicationSource{ 196 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 197 TargetRevision: "HEAD", 198 Path: "{{path}}", 199 }, 200 Destination: argov1alpha1.ApplicationDestination{ 201 Name: "{{name}}", 202 Namespace: "{{path.basename}}", 203 }, 204 }, 205 }, 206 Generators: []v1alpha1.ApplicationSetGenerator{ 207 { 208 Matrix: &v1alpha1.MatrixGenerator{ 209 Generators: []v1alpha1.ApplicationSetNestedGenerator{ 210 { 211 Clusters: &v1alpha1.ClusterGenerator{ 212 Selector: metav1.LabelSelector{ 213 MatchLabels: map[string]string{ 214 "argocd.argoproj.io/secret-type": "cluster", 215 }, 216 }, 217 }, 218 }, 219 { 220 Git: &v1alpha1.GitGenerator{ 221 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 222 Directories: []v1alpha1.GitDirectoryGeneratorItem{ 223 { 224 Path: "*guestbook*", 225 }, 226 }, 227 }, 228 }, 229 }, 230 }, 231 }, 232 }, 233 }, 234 }).Then().Expect(ApplicationsExist(expectedApps)). 235 236 // Update the ApplicationSet template namespace, and verify it updates the Applications 237 When(). 238 And(func() { 239 for _, expectedApp := range expectedApps { 240 newExpectedApp := expectedApp.DeepCopy() 241 newExpectedApp.Spec.Destination.Namespace = "guestbook2" 242 expectedAppsNewNamespace = append(expectedAppsNewNamespace, *newExpectedApp) 243 } 244 }). 245 Update(func(appset *v1alpha1.ApplicationSet) { 246 appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" 247 }).Then().Expect(ApplicationsExist(expectedAppsNewNamespace)). 248 249 // Update the metadata fields in the appset template, and make sure it propagates to the apps 250 When(). 251 And(func() { 252 for _, expectedApp := range expectedAppsNewNamespace { 253 expectedAppNewMetadata := expectedApp.DeepCopy() 254 expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} 255 expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"} 256 expectedAppsNewMetadata = append(expectedAppsNewMetadata, *expectedAppNewMetadata) 257 } 258 }). 259 Update(func(appset *v1alpha1.ApplicationSet) { 260 appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} 261 appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"} 262 }).Then().Expect(ApplicationsExist(expectedAppsNewMetadata)). 263 264 // Delete the ApplicationSet, and verify it deletes the Applications 265 When(). 266 Delete().Then().Expect(ApplicationsDoNotExist(expectedAppsNewNamespace)) 267 } 268 269 func TestMatrixTerminalMatrixGeneratorSelector(t *testing.T) { 270 generateExpectedApp := func(cluster, name string) argov1alpha1.Application { 271 return argov1alpha1.Application{ 272 TypeMeta: metav1.TypeMeta{ 273 Kind: application.ApplicationKind, 274 APIVersion: "argoproj.io/v1alpha1", 275 }, 276 ObjectMeta: metav1.ObjectMeta{ 277 Name: fmt.Sprintf("%s-%s", cluster, name), 278 Namespace: utils.TestNamespace(), 279 Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, 280 }, 281 Spec: argov1alpha1.ApplicationSpec{ 282 Project: "default", 283 Source: &argov1alpha1.ApplicationSource{ 284 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 285 TargetRevision: "HEAD", 286 Path: name, 287 }, 288 Destination: argov1alpha1.ApplicationDestination{ 289 Server: "https://kubernetes.default.svc", 290 Namespace: name, 291 }, 292 }, 293 } 294 } 295 296 expectedApps1 := []argov1alpha1.Application{ 297 generateExpectedApp("cluster1", "kustomize-guestbook"), 298 generateExpectedApp("cluster1", "helm-guestbook"), 299 generateExpectedApp("cluster1", "ksonnet-guestbook"), 300 } 301 expectedApps2 := []argov1alpha1.Application{ 302 generateExpectedApp("cluster2", "kustomize-guestbook"), 303 generateExpectedApp("cluster2", "helm-guestbook"), 304 generateExpectedApp("cluster2", "ksonnet-guestbook"), 305 } 306 307 Given(t). 308 // Create ApplicationSet with LabelSelector on an ApplicationSetTerminalGenerator 309 When(). 310 Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ 311 Name: "matrix-generator-nested-matrix", 312 }, 313 Spec: v1alpha1.ApplicationSetSpec{ 314 ApplyNestedSelectors: true, 315 Template: v1alpha1.ApplicationSetTemplate{ 316 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{values.name}}-{{path.basename}}"}, 317 Spec: argov1alpha1.ApplicationSpec{ 318 Project: "default", 319 Source: &argov1alpha1.ApplicationSource{ 320 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 321 TargetRevision: "HEAD", 322 Path: "{{path}}", 323 }, 324 Destination: argov1alpha1.ApplicationDestination{ 325 Server: "https://kubernetes.default.svc", 326 Namespace: "{{path.basename}}", 327 }, 328 }, 329 }, 330 Generators: []v1alpha1.ApplicationSetGenerator{ 331 { 332 Matrix: &v1alpha1.MatrixGenerator{ 333 Generators: []v1alpha1.ApplicationSetNestedGenerator{ 334 { 335 Matrix: toAPIExtensionsJSON(t, &v1alpha1.NestedMatrixGenerator{ 336 Generators: []v1alpha1.ApplicationSetTerminalGenerator{ 337 { 338 List: &v1alpha1.ListGenerator{ 339 Elements: []apiextensionsv1.JSON{ 340 {Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc", "values": {"name": "cluster1"}}`)}, 341 {Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc", "values": {"name": "cluster2"}}`)}, 342 }, 343 }, 344 Selector: &metav1.LabelSelector{ 345 MatchLabels: map[string]string{ 346 "values.name": "cluster1", 347 }, 348 }, 349 }, 350 { 351 Git: &v1alpha1.GitGenerator{ 352 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 353 Directories: []v1alpha1.GitDirectoryGeneratorItem{ 354 { 355 Path: "*guestbook*", 356 }, 357 }, 358 }, 359 }, 360 }, 361 }), 362 }, 363 { 364 List: &v1alpha1.ListGenerator{ 365 Elements: []apiextensionsv1.JSON{ 366 {Raw: []byte(`{}`)}, 367 }, 368 }, 369 }, 370 }, 371 }, 372 }, 373 }, 374 }, 375 }).Then().Expect(ApplicationsExist(expectedApps1)).Expect(ApplicationsDoNotExist(expectedApps2)). 376 377 // Update the ApplicationSetTerminalGenerator LabelSelector, and verify the Applications are deleted and created 378 When(). 379 Update(func(appset *v1alpha1.ApplicationSet) { 380 appset.Spec.Generators[0].Matrix.Generators[0].Matrix = toAPIExtensionsJSON(t, &v1alpha1.NestedMatrixGenerator{ 381 Generators: []v1alpha1.ApplicationSetTerminalGenerator{ 382 { 383 List: &v1alpha1.ListGenerator{ 384 Elements: []apiextensionsv1.JSON{ 385 {Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc", "values": {"name": "cluster1"}}`)}, 386 {Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc", "values": {"name": "cluster2"}}`)}, 387 }, 388 }, 389 Selector: &metav1.LabelSelector{ 390 MatchLabels: map[string]string{ 391 "values.name": "cluster2", 392 }, 393 }, 394 }, 395 { 396 Git: &v1alpha1.GitGenerator{ 397 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 398 Directories: []v1alpha1.GitDirectoryGeneratorItem{ 399 { 400 Path: "*guestbook*", 401 }, 402 }, 403 }, 404 }, 405 }, 406 }) 407 }).Then().Expect(ApplicationsExist(expectedApps2)).Expect(ApplicationsDoNotExist(expectedApps1)). 408 409 // Set ApplyNestedSelector to false and verify all Applications are created 410 When(). 411 Update(func(appset *v1alpha1.ApplicationSet) { 412 appset.Spec.ApplyNestedSelectors = false 413 }).Then().Expect(ApplicationsExist(expectedApps1)).Expect(ApplicationsExist(expectedApps2)). 414 415 // Delete the ApplicationSet, and verify it deletes the Applications 416 When(). 417 Delete().Then().Expect(ApplicationsDoNotExist(expectedApps1)).Expect(ApplicationsDoNotExist(expectedApps2)) 418 } 419 420 func TestMatrixTerminalMergeGeneratorSelector(t *testing.T) { 421 generateExpectedApp := func(name, nameSuffix string) argov1alpha1.Application { 422 return argov1alpha1.Application{ 423 TypeMeta: metav1.TypeMeta{ 424 Kind: application.ApplicationKind, 425 APIVersion: "argoproj.io/v1alpha1", 426 }, 427 ObjectMeta: metav1.ObjectMeta{ 428 Name: fmt.Sprintf("%s-%s", name, nameSuffix), 429 Namespace: utils.TestNamespace(), 430 Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, 431 }, 432 Spec: argov1alpha1.ApplicationSpec{ 433 Project: "default", 434 Source: &argov1alpha1.ApplicationSource{ 435 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 436 TargetRevision: "HEAD", 437 Path: name, 438 }, 439 Destination: argov1alpha1.ApplicationDestination{ 440 Server: "https://kubernetes.default.svc", 441 Namespace: name, 442 }, 443 }, 444 } 445 } 446 447 expectedApps1 := []argov1alpha1.Application{ 448 generateExpectedApp("kustomize-guestbook", "1"), 449 } 450 expectedApps2 := []argov1alpha1.Application{ 451 generateExpectedApp("helm-guestbook", "2"), 452 } 453 454 Given(t). 455 // Create ApplicationSet with LabelSelector on an ApplicationSetTerminalGenerator 456 When(). 457 Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ 458 Name: "matrix-generator-nested-merge", 459 }, 460 Spec: v1alpha1.ApplicationSetSpec{ 461 ApplyNestedSelectors: true, 462 Template: v1alpha1.ApplicationSetTemplate{ 463 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{path.basename}}-{{name-suffix}}"}, 464 Spec: argov1alpha1.ApplicationSpec{ 465 Project: "default", 466 Source: &argov1alpha1.ApplicationSource{ 467 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 468 TargetRevision: "HEAD", 469 Path: "{{path}}", 470 }, 471 Destination: argov1alpha1.ApplicationDestination{ 472 Server: "https://kubernetes.default.svc", 473 Namespace: "{{path.basename}}", 474 }, 475 }, 476 }, 477 Generators: []v1alpha1.ApplicationSetGenerator{ 478 { 479 Matrix: &v1alpha1.MatrixGenerator{ 480 Generators: []v1alpha1.ApplicationSetNestedGenerator{ 481 { 482 Merge: toAPIExtensionsJSON(t, &v1alpha1.NestedMergeGenerator{ 483 MergeKeys: []string{"path.basename"}, 484 Generators: []v1alpha1.ApplicationSetTerminalGenerator{ 485 { 486 Git: &v1alpha1.GitGenerator{ 487 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 488 Directories: []v1alpha1.GitDirectoryGeneratorItem{ 489 { 490 Path: "*guestbook*", 491 }, 492 }, 493 }, 494 Selector: &metav1.LabelSelector{ 495 MatchLabels: map[string]string{ 496 "path.basename": "kustomize-guestbook", 497 }, 498 }, 499 }, 500 { 501 List: &v1alpha1.ListGenerator{ 502 Elements: []apiextensionsv1.JSON{ 503 {Raw: []byte(`{"path.basename": "kustomize-guestbook", "name-suffix": "1"}`)}, 504 {Raw: []byte(`{"path.basename": "helm-guestbook", "name-suffix": "2"}`)}, 505 }, 506 }, 507 }, 508 }, 509 }), 510 }, 511 { 512 List: &v1alpha1.ListGenerator{ 513 Elements: []apiextensionsv1.JSON{ 514 {Raw: []byte(`{}`)}, 515 }, 516 }, 517 }, 518 }, 519 }, 520 }, 521 }, 522 }, 523 }).Then().Expect(ApplicationsExist(expectedApps1)).Expect(ApplicationsDoNotExist(expectedApps2)). 524 525 // Update the ApplicationSetTerminalGenerator LabelSelector, and verify the Applications are deleted and created 526 When(). 527 Update(func(appset *v1alpha1.ApplicationSet) { 528 529 appset.Spec.Generators[0].Matrix.Generators[0].Merge = toAPIExtensionsJSON(t, &v1alpha1.NestedMergeGenerator{ 530 MergeKeys: []string{"path.basename"}, 531 Generators: []v1alpha1.ApplicationSetTerminalGenerator{ 532 { 533 Git: &v1alpha1.GitGenerator{ 534 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 535 Directories: []v1alpha1.GitDirectoryGeneratorItem{ 536 { 537 Path: "*guestbook*", 538 }, 539 }, 540 }, 541 Selector: &metav1.LabelSelector{ 542 MatchLabels: map[string]string{ 543 "path.basename": "helm-guestbook", 544 }, 545 }, 546 }, 547 { 548 List: &v1alpha1.ListGenerator{ 549 Elements: []apiextensionsv1.JSON{ 550 {Raw: []byte(`{"path.basename": "kustomize-guestbook", "name-suffix": "1"}`)}, 551 {Raw: []byte(`{"path.basename": "helm-guestbook", "name-suffix": "2"}`)}, 552 }, 553 }, 554 }, 555 }, 556 }) 557 }).Then().Expect(ApplicationsExist(expectedApps2)).Expect(ApplicationsDoNotExist(expectedApps1)). 558 559 // Set ApplyNestedSelector to false and verify all Applications are created 560 When(). 561 Update(func(appset *v1alpha1.ApplicationSet) { 562 appset.Spec.ApplyNestedSelectors = false 563 }).Then().Expect(ApplicationsExist(expectedApps1)).Expect(ApplicationsExist(expectedApps2)). 564 565 // Delete the ApplicationSet, and verify it deletes the Applications 566 When(). 567 Delete().Then().Expect(ApplicationsDoNotExist(expectedApps1)).Expect(ApplicationsDoNotExist(expectedApps2)) 568 }