github.com/argoproj/argo-cd/v3@v3.2.1/applicationset/utils/utils_test.go (about) 1 package utils 2 3 import ( 4 "crypto/x509" 5 "encoding/json" 6 "os" 7 "path" 8 "testing" 9 "time" 10 11 "github.com/sirupsen/logrus" 12 logtest "github.com/sirupsen/logrus/hooks/test" 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 16 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 17 "k8s.io/apimachinery/pkg/runtime" 18 "k8s.io/apimachinery/pkg/types" 19 20 argoappsv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 21 ) 22 23 func TestRenderTemplateParams(t *testing.T) { 24 // Believe it or not, this is actually less complex than the equivalent solution using reflection 25 fieldMap := map[string]func(app *argoappsv1.Application) *string{} 26 fieldMap["Path"] = func(app *argoappsv1.Application) *string { return &app.Spec.Source.Path } 27 fieldMap["RepoURL"] = func(app *argoappsv1.Application) *string { return &app.Spec.Source.RepoURL } 28 fieldMap["TargetRevision"] = func(app *argoappsv1.Application) *string { return &app.Spec.Source.TargetRevision } 29 fieldMap["Chart"] = func(app *argoappsv1.Application) *string { return &app.Spec.Source.Chart } 30 31 fieldMap["Server"] = func(app *argoappsv1.Application) *string { return &app.Spec.Destination.Server } 32 fieldMap["Namespace"] = func(app *argoappsv1.Application) *string { return &app.Spec.Destination.Namespace } 33 fieldMap["Name"] = func(app *argoappsv1.Application) *string { return &app.Spec.Destination.Name } 34 35 fieldMap["Project"] = func(app *argoappsv1.Application) *string { return &app.Spec.Project } 36 37 emptyApplication := &argoappsv1.Application{ 38 ObjectMeta: metav1.ObjectMeta{ 39 Annotations: map[string]string{"annotation-key": "annotation-value", "annotation-key2": "annotation-value2"}, 40 Labels: map[string]string{"label-key": "label-value", "label-key2": "label-value2"}, 41 CreationTimestamp: metav1.NewTime(time.Now()), 42 UID: types.UID("d546da12-06b7-4f9a-8ea2-3adb16a20e2b"), 43 Name: "application-one", 44 Namespace: "default", 45 }, 46 Spec: argoappsv1.ApplicationSpec{ 47 Source: &argoappsv1.ApplicationSource{ 48 Path: "", 49 RepoURL: "", 50 TargetRevision: "", 51 Chart: "", 52 }, 53 Destination: argoappsv1.ApplicationDestination{ 54 Server: "", 55 Namespace: "", 56 Name: "", 57 }, 58 Project: "", 59 }, 60 } 61 62 tests := []struct { 63 name string 64 fieldVal string 65 params map[string]any 66 expectedVal string 67 }{ 68 { 69 name: "simple substitution", 70 fieldVal: "{{one}}", 71 expectedVal: "two", 72 params: map[string]any{ 73 "one": "two", 74 }, 75 }, 76 { 77 name: "simple substitution with whitespace", 78 fieldVal: "{{ one }}", 79 expectedVal: "two", 80 params: map[string]any{ 81 "one": "two", 82 }, 83 }, 84 85 { 86 name: "template characters but not in a template", 87 fieldVal: "}} {{", 88 expectedVal: "}} {{", 89 params: map[string]any{ 90 "one": "two", 91 }, 92 }, 93 94 { 95 name: "nested template", 96 fieldVal: "{{ }}", 97 expectedVal: "{{ }}", 98 params: map[string]any{ 99 "one": "{{ }}", 100 }, 101 }, 102 { 103 name: "field with whitespace", 104 fieldVal: "{{ }}", 105 expectedVal: "{{ }}", 106 params: map[string]any{ 107 " ": "two", 108 "": "three", 109 }, 110 }, 111 112 { 113 name: "template contains itself, containing itself", 114 fieldVal: "{{one}}", 115 expectedVal: "{{one}}", 116 params: map[string]any{ 117 "{{one}}": "{{one}}", 118 }, 119 }, 120 121 { 122 name: "template contains itself, containing something else", 123 fieldVal: "{{one}}", 124 expectedVal: "{{one}}", 125 params: map[string]any{ 126 "{{one}}": "{{two}}", 127 }, 128 }, 129 130 { 131 name: "templates are case sensitive", 132 fieldVal: "{{ONE}}", 133 expectedVal: "{{ONE}}", 134 params: map[string]any{ 135 "{{one}}": "two", 136 }, 137 }, 138 { 139 name: "multiple on a line", 140 fieldVal: "{{one}}{{one}}", 141 expectedVal: "twotwo", 142 params: map[string]any{ 143 "one": "two", 144 }, 145 }, 146 { 147 name: "multiple different on a line", 148 fieldVal: "{{one}}{{three}}", 149 expectedVal: "twofour", 150 params: map[string]any{ 151 "one": "two", 152 "three": "four", 153 }, 154 }, 155 { 156 name: "multiple different on a line with quote", 157 fieldVal: "{{one}} {{three}}", 158 expectedVal: "\"hello\" world four", 159 params: map[string]any{ 160 "one": "\"hello\" world", 161 "three": "four", 162 }, 163 }, 164 } 165 166 for _, test := range tests { 167 t.Run(test.name, func(t *testing.T) { 168 for fieldName, getPtrFunc := range fieldMap { 169 // Clone the template application 170 application := emptyApplication.DeepCopy() 171 172 // Set the value of the target field, to the test value 173 *getPtrFunc(application) = test.fieldVal 174 175 // Render the cloned application, into a new application 176 render := Render{} 177 newApplication, err := render.RenderTemplateParams(application, nil, test.params, false, nil) 178 179 // Retrieve the value of the target field from the newApplication, then verify that 180 // the target field has been templated into the expected value 181 actualValue := *getPtrFunc(newApplication) 182 assert.Equal(t, test.expectedVal, actualValue, "Field '%s' had an unexpected value. expected: '%s' value: '%s'", fieldName, test.expectedVal, actualValue) 183 assert.Equal(t, "annotation-value", newApplication.Annotations["annotation-key"]) 184 assert.Equal(t, "annotation-value2", newApplication.Annotations["annotation-key2"]) 185 assert.Equal(t, "label-value", newApplication.Labels["label-key"]) 186 assert.Equal(t, "label-value2", newApplication.Labels["label-key2"]) 187 assert.Equal(t, "application-one", newApplication.Name) 188 assert.Equal(t, "default", newApplication.Namespace) 189 assert.Equal(t, newApplication.UID, types.UID("d546da12-06b7-4f9a-8ea2-3adb16a20e2b")) 190 assert.Equal(t, newApplication.CreationTimestamp, application.CreationTimestamp) 191 require.NoError(t, err) 192 } 193 }) 194 } 195 } 196 197 func TestRenderHelmValuesObjectJson(t *testing.T) { 198 params := map[string]any{ 199 "test": "Hello world", 200 } 201 202 application := &argoappsv1.Application{ 203 ObjectMeta: metav1.ObjectMeta{ 204 Annotations: map[string]string{"annotation-key": "annotation-value", "annotation-key2": "annotation-value2"}, 205 Labels: map[string]string{"label-key": "label-value", "label-key2": "label-value2"}, 206 CreationTimestamp: metav1.NewTime(time.Now()), 207 UID: types.UID("d546da12-06b7-4f9a-8ea2-3adb16a20e2b"), 208 Name: "application-one", 209 Namespace: "default", 210 }, 211 Spec: argoappsv1.ApplicationSpec{ 212 Source: &argoappsv1.ApplicationSource{ 213 Path: "", 214 RepoURL: "", 215 TargetRevision: "", 216 Chart: "", 217 Helm: &argoappsv1.ApplicationSourceHelm{ 218 ValuesObject: &runtime.RawExtension{ 219 Raw: []byte(`{ 220 "some": { 221 "string": "{{.test}}" 222 } 223 }`), 224 }, 225 }, 226 }, 227 Destination: argoappsv1.ApplicationDestination{ 228 Server: "", 229 Namespace: "", 230 Name: "", 231 }, 232 Project: "", 233 }, 234 } 235 236 // Render the cloned application, into a new application 237 render := Render{} 238 newApplication, err := render.RenderTemplateParams(application, nil, params, true, []string{}) 239 240 require.NoError(t, err) 241 assert.NotNil(t, newApplication) 242 243 var unmarshaled any 244 err = json.Unmarshal(newApplication.Spec.Source.Helm.ValuesObject.Raw, &unmarshaled) 245 246 require.NoError(t, err) 247 assert.Equal(t, "Hello world", unmarshaled.(map[string]any)["some"].(map[string]any)["string"]) 248 } 249 250 func TestRenderHelmValuesObjectYaml(t *testing.T) { 251 params := map[string]any{ 252 "test": "Hello world", 253 } 254 255 application := &argoappsv1.Application{ 256 ObjectMeta: metav1.ObjectMeta{ 257 Annotations: map[string]string{"annotation-key": "annotation-value", "annotation-key2": "annotation-value2"}, 258 Labels: map[string]string{"label-key": "label-value", "label-key2": "label-value2"}, 259 CreationTimestamp: metav1.NewTime(time.Now()), 260 UID: types.UID("d546da12-06b7-4f9a-8ea2-3adb16a20e2b"), 261 Name: "application-one", 262 Namespace: "default", 263 }, 264 Spec: argoappsv1.ApplicationSpec{ 265 Source: &argoappsv1.ApplicationSource{ 266 Path: "", 267 RepoURL: "", 268 TargetRevision: "", 269 Chart: "", 270 Helm: &argoappsv1.ApplicationSourceHelm{ 271 ValuesObject: &runtime.RawExtension{ 272 Raw: []byte(`some: 273 string: "{{.test}}"`), 274 }, 275 }, 276 }, 277 Destination: argoappsv1.ApplicationDestination{ 278 Server: "", 279 Namespace: "", 280 Name: "", 281 }, 282 Project: "", 283 }, 284 } 285 286 // Render the cloned application, into a new application 287 render := Render{} 288 newApplication, err := render.RenderTemplateParams(application, nil, params, true, []string{}) 289 290 require.NoError(t, err) 291 assert.NotNil(t, newApplication) 292 293 var unmarshaled any 294 err = json.Unmarshal(newApplication.Spec.Source.Helm.ValuesObject.Raw, &unmarshaled) 295 296 require.NoError(t, err) 297 assert.Equal(t, "Hello world", unmarshaled.(map[string]any)["some"].(map[string]any)["string"]) 298 } 299 300 func TestRenderTemplateParamsGoTemplate(t *testing.T) { 301 // Believe it or not, this is actually less complex than the equivalent solution using reflection 302 fieldMap := map[string]func(app *argoappsv1.Application) *string{} 303 fieldMap["Path"] = func(app *argoappsv1.Application) *string { return &app.Spec.Source.Path } 304 fieldMap["RepoURL"] = func(app *argoappsv1.Application) *string { return &app.Spec.Source.RepoURL } 305 fieldMap["TargetRevision"] = func(app *argoappsv1.Application) *string { return &app.Spec.Source.TargetRevision } 306 fieldMap["Chart"] = func(app *argoappsv1.Application) *string { return &app.Spec.Source.Chart } 307 308 fieldMap["Server"] = func(app *argoappsv1.Application) *string { return &app.Spec.Destination.Server } 309 fieldMap["Namespace"] = func(app *argoappsv1.Application) *string { return &app.Spec.Destination.Namespace } 310 fieldMap["Name"] = func(app *argoappsv1.Application) *string { return &app.Spec.Destination.Name } 311 312 fieldMap["Project"] = func(app *argoappsv1.Application) *string { return &app.Spec.Project } 313 314 emptyApplication := &argoappsv1.Application{ 315 ObjectMeta: metav1.ObjectMeta{ 316 Annotations: map[string]string{"annotation-key": "annotation-value", "annotation-key2": "annotation-value2"}, 317 Labels: map[string]string{"label-key": "label-value", "label-key2": "label-value2"}, 318 CreationTimestamp: metav1.NewTime(time.Now()), 319 UID: types.UID("d546da12-06b7-4f9a-8ea2-3adb16a20e2b"), 320 Name: "application-one", 321 Namespace: "default", 322 }, 323 Spec: argoappsv1.ApplicationSpec{ 324 Source: &argoappsv1.ApplicationSource{ 325 Path: "", 326 RepoURL: "", 327 TargetRevision: "", 328 Chart: "", 329 }, 330 Destination: argoappsv1.ApplicationDestination{ 331 Server: "", 332 Namespace: "", 333 Name: "", 334 }, 335 Project: "", 336 }, 337 } 338 339 tests := []struct { 340 name string 341 fieldVal string 342 params map[string]any 343 expectedVal string 344 errorMessage string 345 templateOptions []string 346 }{ 347 { 348 name: "simple substitution", 349 fieldVal: "{{ .one }}", 350 expectedVal: "two", 351 params: map[string]any{ 352 "one": "two", 353 }, 354 }, 355 { 356 name: "simple substitution with whitespace", 357 fieldVal: "{{ .one }}", 358 expectedVal: "two", 359 params: map[string]any{ 360 "one": "two", 361 }, 362 }, 363 { 364 name: "template contains itself, containing itself", 365 fieldVal: "{{ .one }}", 366 expectedVal: "{{one}}", 367 params: map[string]any{ 368 "one": "{{one}}", 369 }, 370 }, 371 372 { 373 name: "template contains itself, containing something else", 374 fieldVal: "{{ .one }}", 375 expectedVal: "{{two}}", 376 params: map[string]any{ 377 "one": "{{two}}", 378 }, 379 }, 380 { 381 name: "multiple on a line", 382 fieldVal: "{{.one}}{{.one}}", 383 expectedVal: "twotwo", 384 params: map[string]any{ 385 "one": "two", 386 }, 387 }, 388 { 389 name: "multiple different on a line", 390 fieldVal: "{{.one}}{{.three}}", 391 expectedVal: "twofour", 392 params: map[string]any{ 393 "one": "two", 394 "three": "four", 395 }, 396 }, 397 { 398 name: "multiple different on a line with quote", 399 fieldVal: "{{.one}} {{.three}}", 400 expectedVal: "\"hello\" world four", 401 params: map[string]any{ 402 "one": "\"hello\" world", 403 "three": "four", 404 }, 405 }, 406 { 407 name: "depth", 408 fieldVal: "{{ .image.version }}", 409 expectedVal: "latest", 410 params: map[string]any{ 411 "replicas": 3, 412 "image": map[string]any{ 413 "name": "busybox", 414 "version": "latest", 415 }, 416 }, 417 }, 418 { 419 name: "multiple depth", 420 fieldVal: "{{ .image.name }}:{{ .image.version }}", 421 expectedVal: "busybox:latest", 422 params: map[string]any{ 423 "replicas": 3, 424 "image": map[string]any{ 425 "name": "busybox", 426 "version": "latest", 427 }, 428 }, 429 }, 430 { 431 name: "if ok", 432 fieldVal: "{{ if .hpa.enabled }}{{ .hpa.maxReplicas }}{{ else }}{{ .replicas }}{{ end }}", 433 expectedVal: "5", 434 params: map[string]any{ 435 "replicas": 3, 436 "hpa": map[string]any{ 437 "enabled": true, 438 "minReplicas": 1, 439 "maxReplicas": 5, 440 }, 441 }, 442 }, 443 { 444 name: "if not ok", 445 fieldVal: "{{ if .hpa.enabled }}{{ .hpa.maxReplicas }}{{ else }}{{ .replicas }}{{ end }}", 446 expectedVal: "3", 447 params: map[string]any{ 448 "replicas": 3, 449 "hpa": map[string]any{ 450 "enabled": false, 451 "minReplicas": 1, 452 "maxReplicas": 5, 453 }, 454 }, 455 }, 456 { 457 name: "loop", 458 fieldVal: "{{ range .volumes }}[{{ .name }}]{{ end }}", 459 expectedVal: "[volume-one][volume-two]", 460 params: map[string]any{ 461 "replicas": 3, 462 "volumes": []map[string]any{ 463 { 464 "name": "volume-one", 465 "emptyDir": map[string]any{}, 466 }, 467 { 468 "name": "volume-two", 469 "emptyDir": map[string]any{}, 470 }, 471 }, 472 }, 473 }, 474 { 475 name: "Index", 476 fieldVal: `{{ index .admin "admin-ca" }}, {{ index .admin "admin-jks" }}`, 477 expectedVal: "value admin ca, value admin jks", 478 params: map[string]any{ 479 "admin": map[string]any{ 480 "admin-ca": "value admin ca", 481 "admin-jks": "value admin jks", 482 }, 483 }, 484 }, 485 { 486 name: "Index", 487 fieldVal: `{{ index .admin "admin-ca" }}, \\ "Hello world", {{ index .admin "admin-jks" }}`, 488 expectedVal: `value "admin" ca with \, \\ "Hello world", value admin jks`, 489 params: map[string]any{ 490 "admin": map[string]any{ 491 "admin-ca": `value "admin" ca with \`, 492 "admin-jks": "value admin jks", 493 }, 494 }, 495 }, 496 { 497 name: "quote", 498 fieldVal: `{{.quote}}`, 499 expectedVal: `"`, 500 params: map[string]any{ 501 "quote": `"`, 502 }, 503 }, 504 { 505 name: "Test No Data", 506 fieldVal: `{{.data}}`, 507 expectedVal: "{{.data}}", 508 params: map[string]any{}, 509 }, 510 { 511 name: "Test Parse Error", 512 fieldVal: `{{functiondoesnotexist}}`, 513 expectedVal: "", 514 params: map[string]any{ 515 "data": `a data string`, 516 }, 517 errorMessage: `failed to parse template {{functiondoesnotexist}}: template: :1: function "functiondoesnotexist" not defined`, 518 }, 519 { 520 name: "Test template error", 521 fieldVal: `{{.data.test}}`, 522 expectedVal: "", 523 params: map[string]any{ 524 "data": `a data string`, 525 }, 526 errorMessage: `failed to execute go template {{.data.test}}: template: :1:7: executing "" at <.data.test>: can't evaluate field test in type interface {}`, 527 }, 528 { 529 name: "lookup missing value with missingkey=default", 530 fieldVal: `--> {{.doesnotexist}} <--`, 531 expectedVal: `--> <no value> <--`, 532 params: map[string]any{ 533 // if no params are passed then for some reason templating is skipped 534 "unused": "this is not used", 535 }, 536 }, 537 { 538 name: "lookup missing value with missingkey=error", 539 fieldVal: `--> {{.doesnotexist}} <--`, 540 expectedVal: "", 541 params: map[string]any{ 542 // if no params are passed then for some reason templating is skipped 543 "unused": "this is not used", 544 }, 545 templateOptions: []string{"missingkey=error"}, 546 errorMessage: `failed to execute go template --> {{.doesnotexist}} <--: template: :1:6: executing "" at <.doesnotexist>: map has no entry for key "doesnotexist"`, 547 }, 548 { 549 name: "toYaml", 550 fieldVal: `{{ toYaml . | indent 2 }}`, 551 expectedVal: " foo:\n bar:\n bool: true\n number: 2\n str: Hello world", 552 params: map[string]any{ 553 "foo": map[string]any{ 554 "bar": map[string]any{ 555 "bool": true, 556 "number": 2, 557 "str": "Hello world", 558 }, 559 }, 560 }, 561 }, 562 { 563 name: "toYaml Error", 564 fieldVal: `{{ toYaml . | indent 2 }}`, 565 expectedVal: " foo:\n bar:\n bool: true\n number: 2\n str: Hello world", 566 errorMessage: "failed to execute go template {{ toYaml . | indent 2 }}: template: :1:3: executing \"\" at <toYaml .>: error calling toYaml: error marshaling into JSON: json: unsupported type: func(*string)", 567 params: map[string]any{ 568 "foo": func(_ *string) { 569 }, 570 }, 571 }, 572 { 573 name: "fromYaml", 574 fieldVal: `{{ get (fromYaml .value) "hello" }}`, 575 expectedVal: "world", 576 params: map[string]any{ 577 "value": "hello: world", 578 }, 579 }, 580 { 581 name: "fromYaml error", 582 fieldVal: `{{ get (fromYaml .value) "hello" }}`, 583 expectedVal: "world", 584 errorMessage: "failed to execute go template {{ get (fromYaml .value) \"hello\" }}: template: :1:8: executing \"\" at <fromYaml .value>: error calling fromYaml: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}", 585 params: map[string]any{ 586 "value": "non\n compliant\n yaml", 587 }, 588 }, 589 { 590 name: "fromYamlArray", 591 fieldVal: `{{ fromYamlArray .value | last }}`, 592 expectedVal: "bonjour tout le monde", 593 params: map[string]any{ 594 "value": "- hello world\n- bonjour tout le monde", 595 }, 596 }, 597 { 598 name: "fromYamlArray error", 599 fieldVal: `{{ fromYamlArray .value | last }}`, 600 expectedVal: "bonjour tout le monde", 601 errorMessage: "failed to execute go template {{ fromYamlArray .value | last }}: template: :1:3: executing \"\" at <fromYamlArray .value>: error calling fromYamlArray: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type []interface {}", 602 params: map[string]any{ 603 "value": "non\n compliant\n yaml", 604 }, 605 }, 606 } 607 608 for _, test := range tests { 609 t.Run(test.name, func(t *testing.T) { 610 for fieldName, getPtrFunc := range fieldMap { 611 // Clone the template application 612 application := emptyApplication.DeepCopy() 613 614 // Set the value of the target field, to the test value 615 *getPtrFunc(application) = test.fieldVal 616 617 // Render the cloned application, into a new application 618 render := Render{} 619 newApplication, err := render.RenderTemplateParams(application, nil, test.params, true, test.templateOptions) 620 621 // Retrieve the value of the target field from the newApplication, then verify that 622 // the target field has been templated into the expected value 623 if test.errorMessage != "" { 624 require.Error(t, err) 625 assert.Equal(t, test.errorMessage, err.Error()) 626 } else { 627 require.NoError(t, err) 628 actualValue := *getPtrFunc(newApplication) 629 assert.Equal(t, test.expectedVal, actualValue, "Field '%s' had an unexpected value. expected: '%s' value: '%s'", fieldName, test.expectedVal, actualValue) 630 assert.Equal(t, "annotation-value", newApplication.Annotations["annotation-key"]) 631 assert.Equal(t, "annotation-value2", newApplication.Annotations["annotation-key2"]) 632 assert.Equal(t, "label-value", newApplication.Labels["label-key"]) 633 assert.Equal(t, "label-value2", newApplication.Labels["label-key2"]) 634 assert.Equal(t, "application-one", newApplication.Name) 635 assert.Equal(t, "default", newApplication.Namespace) 636 assert.Equal(t, newApplication.UID, types.UID("d546da12-06b7-4f9a-8ea2-3adb16a20e2b")) 637 assert.Equal(t, newApplication.CreationTimestamp, application.CreationTimestamp) 638 } 639 } 640 }) 641 } 642 } 643 644 func TestRenderGeneratorParams_does_not_panic(t *testing.T) { 645 // This test verifies that the RenderGeneratorParams function does not panic when the value in a map is a non- 646 // nillable type. This is a regression test. 647 render := Render{} 648 params := map[string]any{ 649 "branch": "master", 650 } 651 generator := &argoappsv1.ApplicationSetGenerator{ 652 Plugin: &argoappsv1.PluginGenerator{ 653 ConfigMapRef: argoappsv1.PluginConfigMapRef{ 654 Name: "cm-plugin", 655 }, 656 Input: argoappsv1.PluginInput{ 657 Parameters: map[string]apiextensionsv1.JSON{ 658 "branch": { 659 Raw: []byte(`"{{.branch}}"`), 660 }, 661 "repo": { 662 Raw: []byte(`"argo-test"`), 663 }, 664 }, 665 }, 666 }, 667 } 668 _, err := render.RenderGeneratorParams(generator, params, true, []string{}) 669 require.NoError(t, err) 670 } 671 672 func TestRenderTemplateKeys(t *testing.T) { 673 t.Run("fasttemplate", func(t *testing.T) { 674 application := &argoappsv1.Application{ 675 ObjectMeta: metav1.ObjectMeta{ 676 Annotations: map[string]string{ 677 "annotation-{{key}}": "annotation-{{value}}", 678 }, 679 }, 680 } 681 682 params := map[string]any{ 683 "key": "some-key", 684 "value": "some-value", 685 } 686 687 render := Render{} 688 newApplication, err := render.RenderTemplateParams(application, nil, params, false, nil) 689 require.NoError(t, err) 690 require.Contains(t, newApplication.Annotations, "annotation-some-key") 691 assert.Equal(t, "annotation-some-value", newApplication.Annotations["annotation-some-key"]) 692 }) 693 t.Run("gotemplate", func(t *testing.T) { 694 application := &argoappsv1.Application{ 695 ObjectMeta: metav1.ObjectMeta{ 696 Annotations: map[string]string{ 697 "annotation-{{ .key }}": "annotation-{{ .value }}", 698 }, 699 }, 700 } 701 702 params := map[string]any{ 703 "key": "some-key", 704 "value": "some-value", 705 } 706 707 render := Render{} 708 newApplication, err := render.RenderTemplateParams(application, nil, params, true, nil) 709 require.NoError(t, err) 710 require.Contains(t, newApplication.Annotations, "annotation-some-key") 711 assert.Equal(t, "annotation-some-value", newApplication.Annotations["annotation-some-key"]) 712 }) 713 } 714 715 func Test_Render_Replace_no_panic_on_missing_closing_brace(t *testing.T) { 716 r := &Render{} 717 assert.NotPanics(t, func() { 718 _, err := r.Replace("{{properly.closed}} {{improperly.closed}", nil, false, []string{}) 719 require.Error(t, err) 720 }) 721 } 722 723 func TestRenderTemplateParamsFinalizers(t *testing.T) { 724 emptyApplication := &argoappsv1.Application{ 725 Spec: argoappsv1.ApplicationSpec{ 726 Source: &argoappsv1.ApplicationSource{ 727 Path: "", 728 RepoURL: "", 729 TargetRevision: "", 730 Chart: "", 731 }, 732 Destination: argoappsv1.ApplicationDestination{ 733 Server: "", 734 Namespace: "", 735 Name: "", 736 }, 737 Project: "", 738 }, 739 } 740 741 for _, c := range []struct { 742 testName string 743 syncPolicy *argoappsv1.ApplicationSetSyncPolicy 744 existingFinalizers []string 745 expectedFinalizers []string 746 }{ 747 { 748 testName: "existing finalizer should be preserved", 749 existingFinalizers: []string{"existing-finalizer"}, 750 syncPolicy: nil, 751 expectedFinalizers: []string{"existing-finalizer"}, 752 }, 753 { 754 testName: "background finalizer should be preserved", 755 existingFinalizers: []string{argoappsv1.BackgroundPropagationPolicyFinalizer}, 756 syncPolicy: nil, 757 expectedFinalizers: []string{argoappsv1.BackgroundPropagationPolicyFinalizer}, 758 }, 759 760 { 761 testName: "empty finalizer and empty sync should use standard finalizer", 762 existingFinalizers: nil, 763 syncPolicy: nil, 764 expectedFinalizers: []string{argoappsv1.ResourcesFinalizerName}, 765 }, 766 767 { 768 testName: "standard finalizer should be preserved", 769 existingFinalizers: []string{argoappsv1.ResourcesFinalizerName}, 770 syncPolicy: nil, 771 expectedFinalizers: []string{argoappsv1.ResourcesFinalizerName}, 772 }, 773 { 774 testName: "empty array finalizers should use standard finalizer", 775 existingFinalizers: []string{}, 776 syncPolicy: nil, 777 expectedFinalizers: []string{argoappsv1.ResourcesFinalizerName}, 778 }, 779 { 780 testName: "non-nil sync policy should use standard finalizer", 781 existingFinalizers: nil, 782 syncPolicy: &argoappsv1.ApplicationSetSyncPolicy{}, 783 expectedFinalizers: []string{argoappsv1.ResourcesFinalizerName}, 784 }, 785 { 786 testName: "preserveResourcesOnDeletion should not have a finalizer", 787 existingFinalizers: nil, 788 syncPolicy: &argoappsv1.ApplicationSetSyncPolicy{ 789 PreserveResourcesOnDeletion: true, 790 }, 791 expectedFinalizers: nil, 792 }, 793 { 794 testName: "user-specified finalizer should overwrite preserveResourcesOnDeletion", 795 existingFinalizers: []string{argoappsv1.BackgroundPropagationPolicyFinalizer}, 796 syncPolicy: &argoappsv1.ApplicationSetSyncPolicy{ 797 PreserveResourcesOnDeletion: true, 798 }, 799 expectedFinalizers: []string{argoappsv1.BackgroundPropagationPolicyFinalizer}, 800 }, 801 } { 802 t.Run(c.testName, func(t *testing.T) { 803 // Clone the template application 804 application := emptyApplication.DeepCopy() 805 application.Finalizers = c.existingFinalizers 806 807 params := map[string]any{ 808 "one": "two", 809 } 810 811 // Render the cloned application, into a new application 812 render := Render{} 813 814 res, err := render.RenderTemplateParams(application, c.syncPolicy, params, true, nil) 815 require.NoError(t, err) 816 817 assert.ElementsMatch(t, res.Finalizers, c.expectedFinalizers) 818 }) 819 } 820 } 821 822 func TestCheckInvalidGenerators(t *testing.T) { 823 scheme := runtime.NewScheme() 824 err := argoappsv1.AddToScheme(scheme) 825 require.NoError(t, err) 826 err = argoappsv1.AddToScheme(scheme) 827 require.NoError(t, err) 828 829 for _, c := range []struct { 830 testName string 831 appSet argoappsv1.ApplicationSet 832 expectedMsg string 833 }{ 834 { 835 testName: "invalid generator, without annotation", 836 appSet: argoappsv1.ApplicationSet{ 837 ObjectMeta: metav1.ObjectMeta{ 838 Name: "test-app-set", 839 Namespace: "namespace", 840 }, 841 Spec: argoappsv1.ApplicationSetSpec{ 842 Generators: []argoappsv1.ApplicationSetGenerator{ 843 { 844 List: &argoappsv1.ListGenerator{}, 845 Clusters: nil, 846 Git: nil, 847 }, 848 { 849 List: nil, 850 Clusters: nil, 851 Git: nil, 852 }, 853 { 854 List: nil, 855 Clusters: nil, 856 Git: &argoappsv1.GitGenerator{}, 857 }, 858 }, 859 }, 860 }, 861 expectedMsg: "ApplicationSet test-app-set contains unrecognized generators", 862 }, 863 { 864 testName: "invalid generator, with annotation", 865 appSet: argoappsv1.ApplicationSet{ 866 ObjectMeta: metav1.ObjectMeta{ 867 Name: "test-app-set", 868 Namespace: "namespace", 869 Annotations: map[string]string{ 870 "kubectl.kubernetes.io/last-applied-configuration": `{ 871 "spec":{ 872 "generators":[ 873 {"list":{}}, 874 {"bbb":{}}, 875 {"git":{}}, 876 {"aaa":{}} 877 ] 878 } 879 }`, 880 }, 881 }, 882 Spec: argoappsv1.ApplicationSetSpec{ 883 Generators: []argoappsv1.ApplicationSetGenerator{ 884 { 885 List: &argoappsv1.ListGenerator{}, 886 Clusters: nil, 887 Git: nil, 888 }, 889 { 890 List: nil, 891 Clusters: nil, 892 Git: nil, 893 }, 894 { 895 List: nil, 896 Clusters: nil, 897 Git: &argoappsv1.GitGenerator{}, 898 }, 899 { 900 List: nil, 901 Clusters: nil, 902 Git: nil, 903 }, 904 }, 905 }, 906 }, 907 expectedMsg: "ApplicationSet test-app-set contains unrecognized generators: aaa, bbb", 908 }, 909 } { 910 oldhooks := logrus.StandardLogger().ReplaceHooks(logrus.LevelHooks{}) 911 defer logrus.StandardLogger().ReplaceHooks(oldhooks) 912 hook := logtest.NewGlobal() 913 914 _ = CheckInvalidGenerators(&c.appSet) 915 assert.GreaterOrEqual(t, len(hook.Entries), 1, c.testName) 916 assert.NotNil(t, hook.LastEntry(), c.testName) 917 if hook.LastEntry() != nil { 918 assert.Equal(t, logrus.WarnLevel, hook.LastEntry().Level, c.testName) 919 assert.Equal(t, c.expectedMsg, hook.LastEntry().Message, c.testName) 920 } 921 hook.Reset() 922 } 923 } 924 925 func TestInvalidGenerators(t *testing.T) { 926 scheme := runtime.NewScheme() 927 err := argoappsv1.AddToScheme(scheme) 928 require.NoError(t, err) 929 err = argoappsv1.AddToScheme(scheme) 930 require.NoError(t, err) 931 932 for _, c := range []struct { 933 testName string 934 appSet argoappsv1.ApplicationSet 935 expectedInvalid bool 936 expectedNames map[string]bool 937 }{ 938 { 939 testName: "valid generators, with annotation", 940 appSet: argoappsv1.ApplicationSet{ 941 ObjectMeta: metav1.ObjectMeta{ 942 Name: "name", 943 Namespace: "namespace", 944 Annotations: map[string]string{ 945 "kubectl.kubernetes.io/last-applied-configuration": `{ 946 "spec":{ 947 "generators":[ 948 {"list":{}}, 949 {"cluster":{}}, 950 {"git":{}} 951 ] 952 } 953 }`, 954 }, 955 }, 956 Spec: argoappsv1.ApplicationSetSpec{ 957 Generators: []argoappsv1.ApplicationSetGenerator{ 958 { 959 List: &argoappsv1.ListGenerator{}, 960 Clusters: nil, 961 Git: nil, 962 }, 963 { 964 List: nil, 965 Clusters: &argoappsv1.ClusterGenerator{}, 966 Git: nil, 967 }, 968 { 969 List: nil, 970 Clusters: nil, 971 Git: &argoappsv1.GitGenerator{}, 972 }, 973 }, 974 }, 975 }, 976 expectedInvalid: false, 977 expectedNames: map[string]bool{}, 978 }, 979 { 980 testName: "invalid generators, no annotation", 981 appSet: argoappsv1.ApplicationSet{ 982 ObjectMeta: metav1.ObjectMeta{ 983 Name: "name", 984 Namespace: "namespace", 985 }, 986 Spec: argoappsv1.ApplicationSetSpec{ 987 Generators: []argoappsv1.ApplicationSetGenerator{ 988 { 989 List: nil, 990 Clusters: nil, 991 Git: nil, 992 }, 993 { 994 List: nil, 995 Clusters: nil, 996 Git: nil, 997 }, 998 }, 999 }, 1000 }, 1001 expectedInvalid: true, 1002 expectedNames: map[string]bool{}, 1003 }, 1004 { 1005 testName: "valid and invalid generators, no annotation", 1006 appSet: argoappsv1.ApplicationSet{ 1007 ObjectMeta: metav1.ObjectMeta{ 1008 Name: "name", 1009 Namespace: "namespace", 1010 }, 1011 Spec: argoappsv1.ApplicationSetSpec{ 1012 Generators: []argoappsv1.ApplicationSetGenerator{ 1013 { 1014 List: nil, 1015 Clusters: &argoappsv1.ClusterGenerator{}, 1016 Git: nil, 1017 }, 1018 { 1019 List: nil, 1020 Clusters: nil, 1021 Git: nil, 1022 }, 1023 { 1024 List: nil, 1025 Clusters: nil, 1026 Git: &argoappsv1.GitGenerator{}, 1027 }, 1028 }, 1029 }, 1030 }, 1031 expectedInvalid: true, 1032 expectedNames: map[string]bool{}, 1033 }, 1034 { 1035 testName: "valid and invalid generators, with annotation", 1036 appSet: argoappsv1.ApplicationSet{ 1037 ObjectMeta: metav1.ObjectMeta{ 1038 Name: "name", 1039 Namespace: "namespace", 1040 Annotations: map[string]string{ 1041 "kubectl.kubernetes.io/last-applied-configuration": `{ 1042 "spec":{ 1043 "generators":[ 1044 {"cluster":{}}, 1045 {"bbb":{}}, 1046 {"git":{}}, 1047 {"aaa":{}} 1048 ] 1049 } 1050 }`, 1051 }, 1052 }, 1053 Spec: argoappsv1.ApplicationSetSpec{ 1054 Generators: []argoappsv1.ApplicationSetGenerator{ 1055 { 1056 List: nil, 1057 Clusters: &argoappsv1.ClusterGenerator{}, 1058 Git: nil, 1059 }, 1060 { 1061 List: nil, 1062 Clusters: nil, 1063 Git: nil, 1064 }, 1065 { 1066 List: nil, 1067 Clusters: nil, 1068 Git: &argoappsv1.GitGenerator{}, 1069 }, 1070 { 1071 List: nil, 1072 Clusters: nil, 1073 Git: nil, 1074 }, 1075 }, 1076 }, 1077 }, 1078 expectedInvalid: true, 1079 expectedNames: map[string]bool{ 1080 "aaa": true, 1081 "bbb": true, 1082 }, 1083 }, 1084 { 1085 testName: "invalid generator, annotation with missing spec", 1086 appSet: argoappsv1.ApplicationSet{ 1087 ObjectMeta: metav1.ObjectMeta{ 1088 Name: "name", 1089 Namespace: "namespace", 1090 Annotations: map[string]string{ 1091 "kubectl.kubernetes.io/last-applied-configuration": `{ 1092 }`, 1093 }, 1094 }, 1095 Spec: argoappsv1.ApplicationSetSpec{ 1096 Generators: []argoappsv1.ApplicationSetGenerator{ 1097 { 1098 List: nil, 1099 Clusters: nil, 1100 Git: nil, 1101 }, 1102 }, 1103 }, 1104 }, 1105 expectedInvalid: true, 1106 expectedNames: map[string]bool{}, 1107 }, 1108 { 1109 testName: "invalid generator, annotation with missing generators array", 1110 appSet: argoappsv1.ApplicationSet{ 1111 ObjectMeta: metav1.ObjectMeta{ 1112 Name: "name", 1113 Namespace: "namespace", 1114 Annotations: map[string]string{ 1115 "kubectl.kubernetes.io/last-applied-configuration": `{ 1116 "spec":{ 1117 } 1118 }`, 1119 }, 1120 }, 1121 Spec: argoappsv1.ApplicationSetSpec{ 1122 Generators: []argoappsv1.ApplicationSetGenerator{ 1123 { 1124 List: nil, 1125 Clusters: nil, 1126 Git: nil, 1127 }, 1128 }, 1129 }, 1130 }, 1131 expectedInvalid: true, 1132 expectedNames: map[string]bool{}, 1133 }, 1134 { 1135 testName: "invalid generator, annotation with empty generators array", 1136 appSet: argoappsv1.ApplicationSet{ 1137 ObjectMeta: metav1.ObjectMeta{ 1138 Name: "name", 1139 Namespace: "namespace", 1140 Annotations: map[string]string{ 1141 "kubectl.kubernetes.io/last-applied-configuration": `{ 1142 "spec":{ 1143 "generators":[ 1144 ] 1145 } 1146 }`, 1147 }, 1148 }, 1149 Spec: argoappsv1.ApplicationSetSpec{ 1150 Generators: []argoappsv1.ApplicationSetGenerator{ 1151 { 1152 List: nil, 1153 Clusters: nil, 1154 Git: nil, 1155 }, 1156 }, 1157 }, 1158 }, 1159 expectedInvalid: true, 1160 expectedNames: map[string]bool{}, 1161 }, 1162 { 1163 testName: "invalid generator, annotation with empty generator", 1164 appSet: argoappsv1.ApplicationSet{ 1165 ObjectMeta: metav1.ObjectMeta{ 1166 Name: "name", 1167 Namespace: "namespace", 1168 Annotations: map[string]string{ 1169 "kubectl.kubernetes.io/last-applied-configuration": `{ 1170 "spec":{ 1171 "generators":[ 1172 {} 1173 ] 1174 } 1175 }`, 1176 }, 1177 }, 1178 Spec: argoappsv1.ApplicationSetSpec{ 1179 Generators: []argoappsv1.ApplicationSetGenerator{ 1180 { 1181 List: nil, 1182 Clusters: nil, 1183 Git: nil, 1184 }, 1185 }, 1186 }, 1187 }, 1188 expectedInvalid: true, 1189 expectedNames: map[string]bool{}, 1190 }, 1191 } { 1192 hasInvalid, names := invalidGenerators(&c.appSet) 1193 assert.Equal(t, c.expectedInvalid, hasInvalid, c.testName) 1194 assert.Equal(t, c.expectedNames, names, c.testName) 1195 } 1196 } 1197 1198 func TestNormalizeBitbucketBasePath(t *testing.T) { 1199 for _, c := range []struct { 1200 testName string 1201 basePath string 1202 expectedBasePath string 1203 }{ 1204 { 1205 testName: "default api url", 1206 basePath: "https://company.bitbucket.com", 1207 expectedBasePath: "https://company.bitbucket.com/rest", 1208 }, 1209 { 1210 testName: "with /rest suffix", 1211 basePath: "https://company.bitbucket.com/rest", 1212 expectedBasePath: "https://company.bitbucket.com/rest", 1213 }, 1214 { 1215 testName: "with /rest/ suffix", 1216 basePath: "https://company.bitbucket.com/rest/", 1217 expectedBasePath: "https://company.bitbucket.com/rest", 1218 }, 1219 } { 1220 result := NormalizeBitbucketBasePath(c.basePath) 1221 assert.Equal(t, c.expectedBasePath, result, c.testName) 1222 } 1223 } 1224 1225 func TestSlugify(t *testing.T) { 1226 for _, c := range []struct { 1227 branch string 1228 smartTruncate bool 1229 length int 1230 expectedBasePath string 1231 }{ 1232 { 1233 branch: "feat/a_really+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature", 1234 smartTruncate: false, 1235 length: 50, 1236 expectedBasePath: "feat-a-really-long-pull-request-name-to-test-argo", 1237 }, 1238 { 1239 branch: "feat/a_really+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature", 1240 smartTruncate: true, 1241 length: 53, 1242 expectedBasePath: "feat-a-really-long-pull-request-name-to-test-argo", 1243 }, 1244 { 1245 branch: "feat/areallylongpullrequestnametotestargoslugificationandbranchnameshorteningfeature", 1246 smartTruncate: true, 1247 length: 50, 1248 expectedBasePath: "feat", 1249 }, 1250 { 1251 branch: "feat/areallylongpullrequestnametotestargoslugificationandbranchnameshorteningfeature", 1252 smartTruncate: false, 1253 length: 50, 1254 expectedBasePath: "feat-areallylongpullrequestnametotestargoslugifica", 1255 }, 1256 } { 1257 result := SlugifyName(c.length, c.smartTruncate, c.branch) 1258 assert.Equal(t, c.expectedBasePath, result, c.branch) 1259 } 1260 } 1261 1262 func TestGetTLSConfig(t *testing.T) { 1263 temppath := t.TempDir() 1264 certFromFile := ` 1265 -----BEGIN CERTIFICATE----- 1266 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 1297 -----END CERTIFICATE----- 1298 ` 1299 1300 certFromCM := ` 1301 -----BEGIN CERTIFICATE----- 1302 MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS 1303 MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw 1304 MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 1305 MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r 1306 bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U 1307 aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P 1308 YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk 1309 POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu 1310 h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE 1311 AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud 1312 DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv 1313 bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI 1314 5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv 1315 cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2 1316 +tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B 1317 grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK 1318 5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/ 1319 WkBKOclmOV2xlTVuPw== 1320 -----END CERTIFICATE----- 1321 ` 1322 1323 rootCAPath := path.Join(temppath, "foo.example.com") 1324 err := os.WriteFile(rootCAPath, []byte(certFromFile), 0o666) 1325 if err != nil { 1326 panic(err) 1327 } 1328 1329 testCases := []struct { 1330 name string 1331 scmRootCAPath string 1332 insecure bool 1333 caCerts []byte 1334 validateCertInTLSConfig bool 1335 }{ 1336 { 1337 name: "Insecure mode configured, SCM Root CA Path not set", 1338 scmRootCAPath: "", 1339 insecure: true, 1340 caCerts: nil, 1341 validateCertInTLSConfig: false, 1342 }, 1343 { 1344 name: "SCM Root CA Path set, Insecure mode set to false", 1345 scmRootCAPath: rootCAPath, 1346 insecure: false, 1347 caCerts: nil, 1348 validateCertInTLSConfig: true, 1349 }, 1350 { 1351 name: "SCM Root CA Path set, Insecure mode set to true", 1352 scmRootCAPath: rootCAPath, 1353 insecure: true, 1354 caCerts: nil, 1355 validateCertInTLSConfig: true, 1356 }, 1357 { 1358 name: "Cert passed, Insecure mode set to false", 1359 scmRootCAPath: "", 1360 insecure: false, 1361 caCerts: []byte(certFromCM), 1362 validateCertInTLSConfig: true, 1363 }, 1364 { 1365 name: "SCM Root CA Path set, cert passed, Insecure mode set to false", 1366 scmRootCAPath: rootCAPath, 1367 insecure: false, 1368 caCerts: []byte(certFromCM), 1369 validateCertInTLSConfig: true, 1370 }, 1371 } 1372 1373 for _, testCase := range testCases { 1374 t.Run(testCase.name, func(t *testing.T) { 1375 certPool := x509.NewCertPool() 1376 tlsConfig := GetTlsConfig(testCase.scmRootCAPath, testCase.insecure, testCase.caCerts) 1377 assert.Equal(t, testCase.insecure, tlsConfig.InsecureSkipVerify) 1378 if testCase.caCerts != nil { 1379 ok := certPool.AppendCertsFromPEM([]byte(certFromCM)) 1380 assert.True(t, ok) 1381 } 1382 if testCase.scmRootCAPath != "" { 1383 ok := certPool.AppendCertsFromPEM([]byte(certFromFile)) 1384 assert.True(t, ok) 1385 } 1386 assert.NotNil(t, tlsConfig) 1387 if testCase.validateCertInTLSConfig { 1388 assert.True(t, tlsConfig.RootCAs.Equal(certPool)) 1389 } 1390 }) 1391 } 1392 }