github.com/argoproj/argo-cd/v2@v2.10.5/test/e2e/merge_e2e_test.go (about) 1 package e2e 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "testing" 7 8 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 9 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 11 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 12 argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 13 . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets" 14 "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils" 15 16 "github.com/argoproj/argo-cd/v2/pkg/apis/application" 17 ) 18 19 func TestListMergeGenerator(t *testing.T) { 20 generateExpectedApp := func(name, nameSuffix string) argov1alpha1.Application { 21 return argov1alpha1.Application{ 22 TypeMeta: metav1.TypeMeta{ 23 Kind: application.ApplicationKind, 24 APIVersion: "argoproj.io/v1alpha1", 25 }, 26 ObjectMeta: metav1.ObjectMeta{ 27 Name: fmt.Sprintf("%s-%s", name, nameSuffix), 28 Namespace: utils.TestNamespace(), 29 Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, 30 }, 31 Spec: argov1alpha1.ApplicationSpec{ 32 Project: "default", 33 Source: &argov1alpha1.ApplicationSource{ 34 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 35 TargetRevision: "HEAD", 36 Path: name, 37 }, 38 Destination: argov1alpha1.ApplicationDestination{ 39 Server: "https://kubernetes.default.svc", 40 Namespace: name, 41 }, 42 }, 43 } 44 } 45 46 expectedApps := []argov1alpha1.Application{ 47 generateExpectedApp("kustomize-guestbook", "1"), 48 generateExpectedApp("helm-guestbook", "2"), 49 } 50 51 var expectedAppsNewNamespace []argov1alpha1.Application 52 var expectedAppsNewMetadata []argov1alpha1.Application 53 54 Given(t). 55 // Create a ClusterGenerator-based ApplicationSet 56 When(). 57 Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ 58 Name: "merge-generator", 59 }, 60 Spec: v1alpha1.ApplicationSetSpec{ 61 Template: v1alpha1.ApplicationSetTemplate{ 62 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{path.basename}}-{{name-suffix}}"}, 63 Spec: argov1alpha1.ApplicationSpec{ 64 Project: "default", 65 Source: &argov1alpha1.ApplicationSource{ 66 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 67 TargetRevision: "HEAD", 68 Path: "{{path}}", 69 }, 70 Destination: argov1alpha1.ApplicationDestination{ 71 Server: "https://kubernetes.default.svc", 72 Namespace: "{{path.basename}}", 73 }, 74 }, 75 }, 76 Generators: []v1alpha1.ApplicationSetGenerator{ 77 { 78 Merge: &v1alpha1.MergeGenerator{ 79 MergeKeys: []string{"path.basename"}, 80 Generators: []v1alpha1.ApplicationSetNestedGenerator{ 81 { 82 Git: &v1alpha1.GitGenerator{ 83 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 84 Directories: []v1alpha1.GitDirectoryGeneratorItem{ 85 { 86 Path: "*guestbook*", 87 }, 88 }, 89 }, 90 }, 91 { 92 List: &v1alpha1.ListGenerator{ 93 Elements: []apiextensionsv1.JSON{ 94 {Raw: []byte(`{"path.basename": "kustomize-guestbook", "name-suffix": "1"}`)}, 95 {Raw: []byte(`{"path.basename": "helm-guestbook", "name-suffix": "2"}`)}, 96 }, 97 }, 98 }, 99 }, 100 }, 101 }, 102 }, 103 }, 104 }).Then().Expect(ApplicationsExist(expectedApps)). 105 106 // Update the ApplicationSet template namespace, and verify it updates the Applications 107 When(). 108 And(func() { 109 for _, expectedApp := range expectedApps { 110 newExpectedApp := expectedApp.DeepCopy() 111 newExpectedApp.Spec.Destination.Namespace = "guestbook2" 112 expectedAppsNewNamespace = append(expectedAppsNewNamespace, *newExpectedApp) 113 } 114 }). 115 Update(func(appset *v1alpha1.ApplicationSet) { 116 appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" 117 }).Then().Expect(ApplicationsExist(expectedAppsNewNamespace)). 118 119 // Update the metadata fields in the appset template, and make sure it propagates to the apps 120 When(). 121 And(func() { 122 for _, expectedApp := range expectedAppsNewNamespace { 123 expectedAppNewMetadata := expectedApp.DeepCopy() 124 expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} 125 expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"} 126 expectedAppsNewMetadata = append(expectedAppsNewMetadata, *expectedAppNewMetadata) 127 } 128 }). 129 Update(func(appset *v1alpha1.ApplicationSet) { 130 appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} 131 appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"} 132 }).Then().Expect(ApplicationsExist(expectedAppsNewMetadata)). 133 134 // Delete the ApplicationSet, and verify it deletes the Applications 135 When(). 136 Delete().Then().Expect(ApplicationsDoNotExist(expectedAppsNewNamespace)) 137 } 138 139 func TestClusterMergeGenerator(t *testing.T) { 140 generateExpectedApp := func(cluster, name, nameSuffix string) argov1alpha1.Application { 141 return argov1alpha1.Application{ 142 TypeMeta: metav1.TypeMeta{ 143 Kind: application.ApplicationKind, 144 APIVersion: "argoproj.io/v1alpha1", 145 }, 146 ObjectMeta: metav1.ObjectMeta{ 147 Name: fmt.Sprintf("%s-%s-%s", cluster, name, nameSuffix), 148 Namespace: utils.TestNamespace(), 149 Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, 150 }, 151 Spec: argov1alpha1.ApplicationSpec{ 152 Project: "default", 153 Source: &argov1alpha1.ApplicationSource{ 154 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 155 TargetRevision: "HEAD", 156 Path: name, 157 }, 158 Destination: argov1alpha1.ApplicationDestination{ 159 Name: cluster, 160 Namespace: name, 161 }, 162 }, 163 } 164 } 165 166 expectedApps := []argov1alpha1.Application{ 167 generateExpectedApp("cluster1", "kustomize-guestbook", "1"), 168 generateExpectedApp("cluster1", "helm-guestbook", "0"), 169 generateExpectedApp("cluster1", "ksonnet-guestbook", "0"), 170 171 generateExpectedApp("cluster2", "kustomize-guestbook", "0"), 172 generateExpectedApp("cluster2", "helm-guestbook", "2"), 173 generateExpectedApp("cluster2", "ksonnet-guestbook", "0"), 174 } 175 176 var expectedAppsNewNamespace []argov1alpha1.Application 177 var expectedAppsNewMetadata []argov1alpha1.Application 178 179 Given(t). 180 // Create a ClusterGenerator-based ApplicationSet 181 When(). 182 CreateClusterSecret("my-secret", "cluster1", "https://kubernetes.default.svc"). 183 CreateClusterSecret("my-secret2", "cluster2", "https://kubernetes.default.svc"). 184 Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ 185 Name: "merge-generator", 186 }, 187 Spec: v1alpha1.ApplicationSetSpec{ 188 Template: v1alpha1.ApplicationSetTemplate{ 189 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{name}}-{{path.basename}}-{{values.name-suffix}}"}, 190 Spec: argov1alpha1.ApplicationSpec{ 191 Project: "default", 192 Source: &argov1alpha1.ApplicationSource{ 193 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 194 TargetRevision: "HEAD", 195 Path: "{{path}}", 196 }, 197 Destination: argov1alpha1.ApplicationDestination{ 198 Name: "{{name}}", 199 Namespace: "{{path.basename}}", 200 }, 201 }, 202 }, 203 Generators: []v1alpha1.ApplicationSetGenerator{ 204 { 205 Merge: &v1alpha1.MergeGenerator{ 206 MergeKeys: []string{"name", "path.basename"}, 207 Generators: []v1alpha1.ApplicationSetNestedGenerator{ 208 { 209 Matrix: toAPIExtensionsJSON(t, &v1alpha1.NestedMatrixGenerator{ 210 Generators: []v1alpha1.ApplicationSetTerminalGenerator{ 211 { 212 Clusters: &v1alpha1.ClusterGenerator{ 213 Selector: metav1.LabelSelector{ 214 MatchLabels: map[string]string{ 215 "argocd.argoproj.io/secret-type": "cluster", 216 }, 217 }, 218 Values: map[string]string{ 219 "name-suffix": "0", 220 }, 221 }, 222 }, 223 { 224 Git: &v1alpha1.GitGenerator{ 225 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 226 Directories: []v1alpha1.GitDirectoryGeneratorItem{ 227 { 228 Path: "*guestbook*", 229 }, 230 }, 231 }, 232 }, 233 }, 234 }), 235 }, 236 { 237 List: &v1alpha1.ListGenerator{ 238 Elements: []apiextensionsv1.JSON{ 239 {Raw: []byte(`{"name": "cluster1", "path.basename": "kustomize-guestbook", "values": {"name-suffix": "1"}}`)}, 240 {Raw: []byte(`{"name": "cluster2", "path.basename": "helm-guestbook", "values": {"name-suffix": "2"}}`)}, 241 }, 242 }, 243 }, 244 }, 245 }, 246 }, 247 }, 248 }, 249 }).Then().Expect(ApplicationsExist(expectedApps)). 250 251 // Update the ApplicationSet template namespace, and verify it updates the Applications 252 When(). 253 And(func() { 254 for _, expectedApp := range expectedApps { 255 newExpectedApp := expectedApp.DeepCopy() 256 newExpectedApp.Spec.Destination.Namespace = "guestbook2" 257 expectedAppsNewNamespace = append(expectedAppsNewNamespace, *newExpectedApp) 258 } 259 }). 260 Update(func(appset *v1alpha1.ApplicationSet) { 261 appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" 262 }).Then().Expect(ApplicationsExist(expectedAppsNewNamespace)). 263 264 // Update the metadata fields in the appset template, and make sure it propagates to the apps 265 When(). 266 And(func() { 267 for _, expectedApp := range expectedAppsNewNamespace { 268 expectedAppNewMetadata := expectedApp.DeepCopy() 269 expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} 270 expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"} 271 expectedAppsNewMetadata = append(expectedAppsNewMetadata, *expectedAppNewMetadata) 272 } 273 }). 274 Update(func(appset *v1alpha1.ApplicationSet) { 275 appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} 276 appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"} 277 }).Then().Expect(ApplicationsExist(expectedAppsNewMetadata)). 278 279 // Delete the ApplicationSet, and verify it deletes the Applications 280 When(). 281 Delete().Then().Expect(ApplicationsDoNotExist(expectedAppsNewNamespace)) 282 } 283 284 func TestMergeTerminalMergeGeneratorSelector(t *testing.T) { 285 generateExpectedApp := func(name, nameSuffix string) argov1alpha1.Application { 286 return argov1alpha1.Application{ 287 TypeMeta: metav1.TypeMeta{ 288 Kind: application.ApplicationKind, 289 APIVersion: "argoproj.io/v1alpha1", 290 }, 291 ObjectMeta: metav1.ObjectMeta{ 292 Name: fmt.Sprintf("%s-%s", name, nameSuffix), 293 Namespace: utils.TestNamespace(), 294 Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, 295 }, 296 Spec: argov1alpha1.ApplicationSpec{ 297 Project: "default", 298 Source: &argov1alpha1.ApplicationSource{ 299 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 300 TargetRevision: "HEAD", 301 Path: name, 302 }, 303 Destination: argov1alpha1.ApplicationDestination{ 304 Server: "https://kubernetes.default.svc", 305 Namespace: name, 306 }, 307 }, 308 } 309 } 310 311 expectedApps1 := []argov1alpha1.Application{ 312 generateExpectedApp("kustomize-guestbook", "1"), 313 } 314 expectedApps2 := []argov1alpha1.Application{ 315 generateExpectedApp("helm-guestbook", "2"), 316 } 317 318 Given(t). 319 // Create ApplicationSet with LabelSelector on an ApplicationSetTerminalGenerator 320 When(). 321 Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ 322 Name: "merge-generator-nested-merge", 323 }, 324 Spec: v1alpha1.ApplicationSetSpec{ 325 ApplyNestedSelectors: true, 326 Template: v1alpha1.ApplicationSetTemplate{ 327 ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{path.basename}}-{{name-suffix}}"}, 328 Spec: argov1alpha1.ApplicationSpec{ 329 Project: "default", 330 Source: &argov1alpha1.ApplicationSource{ 331 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 332 TargetRevision: "HEAD", 333 Path: "{{path}}", 334 }, 335 Destination: argov1alpha1.ApplicationDestination{ 336 Server: "https://kubernetes.default.svc", 337 Namespace: "{{path.basename}}", 338 }, 339 }, 340 }, 341 Generators: []v1alpha1.ApplicationSetGenerator{ 342 { 343 Merge: &v1alpha1.MergeGenerator{ 344 MergeKeys: []string{"path.basename"}, 345 Generators: []v1alpha1.ApplicationSetNestedGenerator{ 346 { 347 Merge: toAPIExtensionsJSON(t, &v1alpha1.NestedMergeGenerator{ 348 MergeKeys: []string{"path.basename"}, 349 Generators: []v1alpha1.ApplicationSetTerminalGenerator{ 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 Selector: &metav1.LabelSelector{ 360 MatchLabels: map[string]string{ 361 "path.basename": "kustomize-guestbook", 362 }, 363 }, 364 }, 365 { 366 List: &v1alpha1.ListGenerator{ 367 Elements: []apiextensionsv1.JSON{ 368 {Raw: []byte(`{"path.basename": "kustomize-guestbook", "name-suffix": "1"}`)}, 369 {Raw: []byte(`{"path.basename": "helm-guestbook", "name-suffix": "2"}`)}, 370 }, 371 }, 372 }, 373 }, 374 }), 375 }, 376 { 377 List: &v1alpha1.ListGenerator{ 378 Elements: []apiextensionsv1.JSON{ 379 {Raw: []byte(`{}`)}, 380 }, 381 }, 382 }, 383 }, 384 }, 385 }, 386 }, 387 }, 388 }).Then().Expect(ApplicationsExist(expectedApps1)).Expect(ApplicationsDoNotExist(expectedApps2)). 389 390 // Update the ApplicationSetTerminalGenerator LabelSelector, and verify the Applications are deleted and created 391 When(). 392 Update(func(appset *v1alpha1.ApplicationSet) { 393 394 appset.Spec.Generators[0].Merge.Generators[0].Merge = toAPIExtensionsJSON(t, &v1alpha1.NestedMergeGenerator{ 395 MergeKeys: []string{"path.basename"}, 396 Generators: []v1alpha1.ApplicationSetTerminalGenerator{ 397 { 398 Git: &v1alpha1.GitGenerator{ 399 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 400 Directories: []v1alpha1.GitDirectoryGeneratorItem{ 401 { 402 Path: "*guestbook*", 403 }, 404 }, 405 }, 406 Selector: &metav1.LabelSelector{ 407 MatchLabels: map[string]string{ 408 "path.basename": "helm-guestbook", 409 }, 410 }, 411 }, 412 { 413 List: &v1alpha1.ListGenerator{ 414 Elements: []apiextensionsv1.JSON{ 415 {Raw: []byte(`{"path.basename": "kustomize-guestbook", "name-suffix": "1"}`)}, 416 {Raw: []byte(`{"path.basename": "helm-guestbook", "name-suffix": "2"}`)}, 417 }, 418 }, 419 }, 420 }, 421 }) 422 }).Then().Expect(ApplicationsExist(expectedApps2)).Expect(ApplicationsDoNotExist(expectedApps1)). 423 424 // Set ApplyNestedSelector to false and verify all Applications are created 425 When(). 426 Update(func(appset *v1alpha1.ApplicationSet) { 427 appset.Spec.ApplyNestedSelectors = false 428 }).Then().Expect(ApplicationsExist(expectedApps1)).Expect(ApplicationsExist(expectedApps2)). 429 430 // Delete the ApplicationSet, and verify it deletes the Applications 431 When(). 432 Delete().Then().Expect(ApplicationsDoNotExist(expectedApps1)).Expect(ApplicationsDoNotExist(expectedApps2)) 433 } 434 435 func toAPIExtensionsJSON(t *testing.T, g interface{}) *apiextensionsv1.JSON { 436 437 resVal, err := json.Marshal(g) 438 if err != nil { 439 t.Error("unable to unmarshal json", g) 440 return nil 441 } 442 443 res := &apiextensionsv1.JSON{Raw: resVal} 444 445 return res 446 }