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