github.com/argoproj/argo-cd/v3@v3.2.1/util/settings/settings_test.go (about) 1 package settings 2 3 import ( 4 "context" 5 "crypto/tls" 6 "crypto/x509" 7 "fmt" 8 "net/http" 9 "os" 10 "sort" 11 "strings" 12 "testing" 13 "time" 14 15 "github.com/stretchr/testify/assert" 16 "github.com/stretchr/testify/require" 17 corev1 "k8s.io/api/core/v1" 18 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 "k8s.io/apimachinery/pkg/util/yaml" 20 "k8s.io/client-go/kubernetes/fake" 21 22 "github.com/argoproj/argo-cd/v3/common" 23 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 24 testutil "github.com/argoproj/argo-cd/v3/test" 25 "github.com/argoproj/argo-cd/v3/util/test" 26 ) 27 28 func fixtures(data map[string]string, opts ...func(secret *corev1.Secret)) (*fake.Clientset, *SettingsManager) { 29 cm := &corev1.ConfigMap{ 30 ObjectMeta: metav1.ObjectMeta{ 31 Name: common.ArgoCDConfigMapName, 32 Namespace: "default", 33 Labels: map[string]string{ 34 "app.kubernetes.io/part-of": "argocd", 35 }, 36 }, 37 Data: data, 38 } 39 secret := &corev1.Secret{ 40 ObjectMeta: metav1.ObjectMeta{ 41 Name: common.ArgoCDSecretName, 42 Namespace: "default", 43 Labels: map[string]string{ 44 "app.kubernetes.io/part-of": "argocd", 45 }, 46 }, 47 Data: map[string][]byte{}, 48 } 49 for i := range opts { 50 opts[i](secret) 51 } 52 kubeClient := fake.NewClientset(cm, secret) 53 settingsManager := NewSettingsManager(context.Background(), kubeClient, "default") 54 55 return kubeClient, settingsManager 56 } 57 58 func TestDocumentedArgoCDConfigMapIsValid(t *testing.T) { 59 var argocdCM *corev1.ConfigMap 60 settings := ArgoCDSettings{} 61 data, err := os.ReadFile("../../docs/operator-manual/argocd-cm.yaml") 62 require.NoError(t, err) 63 err = yaml.Unmarshal(data, &argocdCM) 64 require.NoError(t, err) 65 updateSettingsFromConfigMap(&settings, argocdCM) 66 } 67 68 func TestGetConfigMapByName(t *testing.T) { 69 t.Run("data is never nil", func(t *testing.T) { 70 _, settingsManager := fixtures(nil) 71 cm, err := settingsManager.GetConfigMapByName(common.ArgoCDConfigMapName) 72 require.NoError(t, err) 73 assert.NotNil(t, cm.Data) 74 }) 75 t.Run("cannot update informer value", func(t *testing.T) { 76 _, settingsManager := fixtures(nil) 77 cm1, err := settingsManager.GetConfigMapByName(common.ArgoCDConfigMapName) 78 require.NoError(t, err) 79 cm1.Data["test"] = "invalid" 80 cm2, err := settingsManager.GetConfigMapByName(common.ArgoCDConfigMapName) 81 require.NoError(t, err) 82 assert.NotContains(t, cm2.Data, "test") 83 }) 84 } 85 86 func TestGetSecretByName(t *testing.T) { 87 t.Run("data is never nil", func(t *testing.T) { 88 _, settingsManager := fixtures(nil, func(secret *corev1.Secret) { secret.Data = nil }) 89 secret, err := settingsManager.GetSecretByName(common.ArgoCDSecretName) 90 require.NoError(t, err) 91 assert.NotNil(t, secret.Data) 92 }) 93 t.Run("cannot update informer value", func(t *testing.T) { 94 _, settingsManager := fixtures(nil) 95 s1, err := settingsManager.GetSecretByName(common.ArgoCDSecretName) 96 require.NoError(t, err) 97 s1.Data["test"] = []byte("invalid") 98 s2, err := settingsManager.GetSecretByName(common.ArgoCDSecretName) 99 require.NoError(t, err) 100 assert.NotContains(t, s2.Data, "test") 101 }) 102 } 103 104 func TestGetExtensionConfigs(t *testing.T) { 105 type cases struct { 106 name string 107 input map[string]string 108 expected map[string]string 109 expectedLen int 110 } 111 112 testCases := []cases{ 113 { 114 name: "will return main config successfully", 115 expectedLen: 1, 116 input: map[string]string{ 117 extensionConfig: "test", 118 }, 119 expected: map[string]string{ 120 "": "test", 121 }, 122 }, 123 { 124 name: "will return main and additional config successfully", 125 expectedLen: 2, 126 input: map[string]string{ 127 extensionConfig: "main config", 128 extensionConfig + ".anotherExtension": "another config", 129 }, 130 expected: map[string]string{ 131 "": "main config", 132 "anotherExtension": "another config", 133 }, 134 }, 135 } 136 137 for _, tc := range testCases { 138 tc := tc 139 t.Run(tc.name, func(t *testing.T) { 140 // When 141 output := getExtensionConfigs(tc.input) 142 143 // Then 144 assert.Len(t, output, tc.expectedLen) 145 assert.Equal(t, tc.expected, output) 146 }) 147 } 148 } 149 150 func TestGetResourceFilter(t *testing.T) { 151 data := map[string]string{ 152 "resource.exclusions": "\n - apiGroups: [\"group1\"]\n kinds: [\"kind1\"]\n clusters: [\"cluster1\"]\n", 153 "resource.inclusions": "\n - apiGroups: [\"group2\"]\n kinds: [\"kind2\"]\n clusters: [\"cluster2\"]\n", 154 } 155 _, settingsManager := fixtures(data) 156 filter, err := settingsManager.GetResourcesFilter() 157 require.NoError(t, err) 158 assert.Equal(t, &ResourcesFilter{ 159 ResourceExclusions: []FilteredResource{{APIGroups: []string{"group1"}, Kinds: []string{"kind1"}, Clusters: []string{"cluster1"}}}, 160 ResourceInclusions: []FilteredResource{{APIGroups: []string{"group2"}, Kinds: []string{"kind2"}, Clusters: []string{"cluster2"}}}, 161 }, filter) 162 } 163 164 func TestInClusterServerAddressEnabled(t *testing.T) { 165 _, settingsManager := fixtures(map[string]string{ 166 "cluster.inClusterEnabled": "true", 167 }) 168 argoCDCM, err := settingsManager.getConfigMap() 169 require.NoError(t, err) 170 assert.Equal(t, "true", argoCDCM.Data[inClusterEnabledKey]) 171 172 _, settingsManager = fixtures(map[string]string{ 173 "cluster.inClusterEnabled": "false", 174 }) 175 argoCDCM, err = settingsManager.getConfigMap() 176 require.NoError(t, err) 177 assert.NotEqual(t, "true", argoCDCM.Data[inClusterEnabledKey]) 178 } 179 180 func TestInClusterServerAddressEnabledByDefault(t *testing.T) { 181 kubeClient := fake.NewClientset( 182 &corev1.ConfigMap{ 183 ObjectMeta: metav1.ObjectMeta{ 184 Name: common.ArgoCDConfigMapName, 185 Namespace: "default", 186 Labels: map[string]string{ 187 "app.kubernetes.io/part-of": "argocd", 188 }, 189 }, 190 Data: map[string]string{}, 191 }, 192 &corev1.Secret{ 193 ObjectMeta: metav1.ObjectMeta{ 194 Name: common.ArgoCDSecretName, 195 Namespace: "default", 196 Labels: map[string]string{ 197 "app.kubernetes.io/part-of": "argocd", 198 }, 199 }, 200 Data: map[string][]byte{ 201 "admin.password": nil, 202 "server.secretkey": nil, 203 }, 204 }, 205 ) 206 settingsManager := NewSettingsManager(t.Context(), kubeClient, "default") 207 settings, err := settingsManager.GetSettings() 208 require.NoError(t, err) 209 assert.True(t, settings.InClusterEnabled) 210 } 211 212 func TestGetAppInstanceLabelKey(t *testing.T) { 213 t.Run("should get custom instanceLabelKey", func(t *testing.T) { 214 _, settingsManager := fixtures(map[string]string{ 215 "application.instanceLabelKey": "testLabel", 216 }) 217 label, err := settingsManager.GetAppInstanceLabelKey() 218 require.NoError(t, err) 219 assert.Equal(t, "testLabel", label) 220 }) 221 222 t.Run("should get default instanceLabelKey if custom not defined", func(t *testing.T) { 223 _, settingsManager := fixtures(map[string]string{}) 224 label, err := settingsManager.GetAppInstanceLabelKey() 225 require.NoError(t, err) 226 assert.Equal(t, common.LabelKeyAppInstance, label) 227 }) 228 } 229 230 func TestGetTrackingMethod(t *testing.T) { 231 t.Run("should get custom trackingMethod", func(t *testing.T) { 232 _, settingsManager := fixtures(map[string]string{ 233 "application.resourceTrackingMethod": string(v1alpha1.TrackingMethodLabel), 234 }) 235 label, err := settingsManager.GetTrackingMethod() 236 require.NoError(t, err) 237 assert.Equal(t, string(v1alpha1.TrackingMethodLabel), label) 238 }) 239 240 t.Run("should get default trackingMethod if custom not defined", func(t *testing.T) { 241 _, settingsManager := fixtures(map[string]string{}) 242 label, err := settingsManager.GetTrackingMethod() 243 require.NoError(t, err) 244 assert.Equal(t, string(v1alpha1.TrackingMethodAnnotation), label) 245 }) 246 } 247 248 func TestGetInstallationID(t *testing.T) { 249 _, settingsManager := fixtures(map[string]string{ 250 "installationID": "123456789", 251 }) 252 id, err := settingsManager.GetInstallationID() 253 require.NoError(t, err) 254 assert.Equal(t, "123456789", id) 255 } 256 257 func TestApplicationFineGrainedRBACInheritanceDisabledDefault(t *testing.T) { 258 _, settingsManager := fixtures(nil) 259 flag, err := settingsManager.ApplicationFineGrainedRBACInheritanceDisabled() 260 require.NoError(t, err) 261 assert.True(t, flag) 262 } 263 264 func TestApplicationFineGrainedRBACInheritanceDisabled(t *testing.T) { 265 _, settingsManager := fixtures(map[string]string{ 266 "server.rbac.disableApplicationFineGrainedRBACInheritance": "false", 267 }) 268 flag, err := settingsManager.ApplicationFineGrainedRBACInheritanceDisabled() 269 require.NoError(t, err) 270 assert.False(t, flag) 271 } 272 273 func TestGetIsIgnoreResourceUpdatesEnabled(t *testing.T) { 274 _, settingsManager := fixtures(nil) 275 ignoreResourceUpdatesEnabled, err := settingsManager.GetIsIgnoreResourceUpdatesEnabled() 276 require.NoError(t, err) 277 assert.True(t, ignoreResourceUpdatesEnabled) 278 279 _, settingsManager = fixtures(map[string]string{ 280 "resource.ignoreResourceUpdatesEnabled": "true", 281 }) 282 ignoreResourceUpdatesEnabled, err = settingsManager.GetIsIgnoreResourceUpdatesEnabled() 283 require.NoError(t, err) 284 assert.True(t, ignoreResourceUpdatesEnabled) 285 } 286 287 func TestGetIsIgnoreResourceUpdatesEnabledFalse(t *testing.T) { 288 _, settingsManager := fixtures(map[string]string{ 289 "resource.ignoreResourceUpdatesEnabled": "false", 290 }) 291 ignoreResourceUpdatesEnabled, err := settingsManager.GetIsIgnoreResourceUpdatesEnabled() 292 require.NoError(t, err) 293 assert.False(t, ignoreResourceUpdatesEnabled) 294 } 295 296 func TestGetResourceOverrides(t *testing.T) { 297 ignoreStatus := v1alpha1.ResourceOverride{IgnoreDifferences: v1alpha1.OverrideIgnoreDiff{ 298 JSONPointers: []string{"/status"}, 299 }} 300 crdGK := "apiextensions.k8s.io/CustomResourceDefinition" 301 302 _, settingsManager := fixtures(map[string]string{ 303 "resource.customizations": ` 304 admissionregistration.k8s.io/MutatingWebhookConfiguration: 305 ignoreDifferences: | 306 jsonPointers: 307 - /webhooks/0/clientConfig/caBundle 308 jqPathExpressions: 309 - .webhooks[0].clientConfig.caBundle 310 ignoreResourceUpdates: | 311 jsonPointers: 312 - /webhooks/1/clientConfig/caBundle 313 jqPathExpressions: 314 - .webhooks[1].clientConfig.caBundle`, 315 }) 316 overrides, err := settingsManager.GetResourceOverrides() 317 require.NoError(t, err) 318 319 webHookOverrides := overrides["admissionregistration.k8s.io/MutatingWebhookConfiguration"] 320 assert.NotNil(t, webHookOverrides) 321 322 assert.Equal(t, v1alpha1.ResourceOverride{ 323 IgnoreDifferences: v1alpha1.OverrideIgnoreDiff{ 324 JSONPointers: []string{"/webhooks/0/clientConfig/caBundle"}, 325 JQPathExpressions: []string{".webhooks[0].clientConfig.caBundle"}, 326 }, 327 IgnoreResourceUpdates: v1alpha1.OverrideIgnoreDiff{ 328 JSONPointers: []string{"/webhooks/1/clientConfig/caBundle"}, 329 JQPathExpressions: []string{".webhooks[1].clientConfig.caBundle"}, 330 }, 331 }, webHookOverrides) 332 333 // by default, all status should be ignored 334 globalOverrides := overrides["*/*"] 335 assert.NotNil(t, globalOverrides) 336 assert.Equal(t, ignoreStatus, globalOverrides) 337 338 // with value all, status of all objects should be ignored 339 _, settingsManager = fixtures(map[string]string{ 340 "resource.compareoptions": ` 341 ignoreResourceStatusField: all`, 342 }) 343 overrides, err = settingsManager.GetResourceOverrides() 344 require.NoError(t, err) 345 346 globalOverrides = overrides["*/*"] 347 assert.NotNil(t, globalOverrides) 348 assert.Equal(t, ignoreStatus, globalOverrides) 349 350 // with value crd, status of crd objects should be ignored 351 _, settingsManager = fixtures(map[string]string{ 352 "resource.compareoptions": ` 353 ignoreResourceStatusField: crd`, 354 355 "resource.customizations": ` 356 apiextensions.k8s.io/CustomResourceDefinition: 357 ignoreDifferences: | 358 jsonPointers: 359 - /webhooks/0/clientConfig/caBundle 360 jqPathExpressions: 361 - .webhooks[0].clientConfig.caBundle`, 362 }) 363 overrides, err = settingsManager.GetResourceOverrides() 364 require.NoError(t, err) 365 366 crdOverrides := overrides[crdGK] 367 assert.NotNil(t, crdOverrides) 368 assert.Equal(t, v1alpha1.ResourceOverride{IgnoreDifferences: v1alpha1.OverrideIgnoreDiff{ 369 JSONPointers: []string{"/webhooks/0/clientConfig/caBundle", "/status"}, 370 JQPathExpressions: []string{".webhooks[0].clientConfig.caBundle"}, 371 }}, crdOverrides) 372 373 // with incorrect value, status of all objects should be ignored 374 _, settingsManager = fixtures(map[string]string{ 375 "resource.compareoptions": ` 376 ignoreResourceStatusField: foobar`, 377 }) 378 overrides, err = settingsManager.GetResourceOverrides() 379 require.NoError(t, err) 380 381 globalOverrides = overrides["*/*"] 382 assert.NotNil(t, globalOverrides) 383 assert.Equal(t, ignoreStatus, globalOverrides) 384 385 // with value non-string off, status of no objects should be ignored 386 _, settingsManager = fixtures(map[string]string{ 387 "resource.compareoptions": ` 388 ignoreResourceStatusField: off`, 389 }) 390 overrides, err = settingsManager.GetResourceOverrides() 391 require.NoError(t, err) 392 assert.Empty(t, overrides) 393 394 // with value non-string false, status of no objects should be ignored 395 _, settingsManager = fixtures(map[string]string{ 396 "resource.compareoptions": ` 397 ignoreResourceStatusField: false`, 398 }) 399 overrides, err = settingsManager.GetResourceOverrides() 400 require.NoError(t, err) 401 assert.Empty(t, overrides) 402 403 // with value none, status of no objects should be ignored 404 _, settingsManager = fixtures(map[string]string{ 405 "resource.compareoptions": ` 406 ignoreResourceStatusField: none`, 407 }) 408 overrides, err = settingsManager.GetResourceOverrides() 409 require.NoError(t, err) 410 assert.Empty(t, overrides) 411 } 412 413 func TestGetResourceOverridesHealthWithWildcard(t *testing.T) { 414 data := map[string]string{ 415 "resource.customizations": ` 416 "*.aws.crossplane.io/*": 417 health.lua: | 418 foo`, 419 } 420 421 t.Run("TestResourceHealthOverrideWithWildcard", func(t *testing.T) { 422 _, settingsManager := fixtures(data) 423 424 overrides, err := settingsManager.GetResourceOverrides() 425 require.NoError(t, err) 426 assert.Len(t, overrides, 2) 427 assert.Equal(t, "foo", overrides["*.aws.crossplane.io/*"].HealthLua) 428 }) 429 } 430 431 func TestSettingsManager_GetResourceOverrides_with_empty_string(t *testing.T) { 432 _, settingsManager := fixtures(map[string]string{ 433 resourceCustomizationsKey: "", 434 }) 435 overrides, err := settingsManager.GetResourceOverrides() 436 require.NoError(t, err) 437 438 assert.Len(t, overrides, 1) 439 } 440 441 func TestGetResourceOverrides_with_splitted_keys(t *testing.T) { 442 data := map[string]string{ 443 "resource.compareoptions": `ignoreResourceStatusField: none`, 444 445 "resource.customizations": ` 446 admissionregistration.k8s.io/MutatingWebhookConfiguration: 447 ignoreDifferences: | 448 jsonPointers: 449 - foo 450 ignoreResourceUpdates: | 451 jsonPointers: 452 - foo 453 certmanager.k8s.io/Certificate: 454 health.lua.useOpenLibs: true 455 health.lua: | 456 foo 457 cert-manager.io/Certificate: 458 health.lua: | 459 foo 460 apps/Deployment: 461 actions: | 462 foo`, 463 } 464 465 t.Run("MergedKey", func(t *testing.T) { 466 _, settingsManager := fixtures(data) 467 468 overrides, err := settingsManager.GetResourceOverrides() 469 require.NoError(t, err) 470 assert.Len(t, overrides, 4) 471 assert.Len(t, overrides["admissionregistration.k8s.io/MutatingWebhookConfiguration"].IgnoreDifferences.JSONPointers, 1) 472 assert.Equal(t, "foo", overrides["admissionregistration.k8s.io/MutatingWebhookConfiguration"].IgnoreDifferences.JSONPointers[0]) 473 assert.Len(t, overrides["admissionregistration.k8s.io/MutatingWebhookConfiguration"].IgnoreResourceUpdates.JSONPointers, 1) 474 assert.Equal(t, "foo", overrides["admissionregistration.k8s.io/MutatingWebhookConfiguration"].IgnoreResourceUpdates.JSONPointers[0]) 475 assert.Equal(t, "foo\n", overrides["certmanager.k8s.io/Certificate"].HealthLua) 476 assert.True(t, overrides["certmanager.k8s.io/Certificate"].UseOpenLibs) 477 assert.Equal(t, "foo\n", overrides["cert-manager.io/Certificate"].HealthLua) 478 assert.False(t, overrides["cert-manager.io/Certificate"].UseOpenLibs) 479 assert.Equal(t, "foo", overrides["apps/Deployment"].Actions) 480 }) 481 482 t.Run("SplitKeys", func(t *testing.T) { 483 newData := map[string]string{ 484 "resource.compareoptions": `ignoreResourceStatusField: none`, 485 486 "resource.customizations.health.admissionregistration.k8s.io_MutatingWebhookConfiguration": "bar", 487 "resource.customizations.ignoreDifferences.admissionregistration.k8s.io_MutatingWebhookConfiguration": `jsonPointers: 488 - bar`, 489 "resource.customizations.ignoreResourceUpdates.admissionregistration.k8s.io_MutatingWebhookConfiguration": `jsonPointers: 490 - bar`, 491 "resource.customizations.knownTypeFields.admissionregistration.k8s.io_MutatingWebhookConfiguration": ` 492 - field: foo 493 type: bar`, 494 "resource.customizations.health.certmanager.k8s.io_Certificate": "bar", 495 "resource.customizations.health.cert-manager.io_Certificate": "bar", 496 "resource.customizations.useOpenLibs.certmanager.k8s.io_Certificate": "false", 497 "resource.customizations.useOpenLibs.cert-manager.io_Certificate": "true", 498 "resource.customizations.actions.apps_Deployment": "bar", 499 "resource.customizations.actions.Deployment": "bar", 500 "resource.customizations.health.iam-manager.k8s.io_Iamrole": "bar", 501 "resource.customizations.health.Iamrole": "bar", 502 "resource.customizations.ignoreDifferences.iam-manager.k8s.io_Iamrole": `jsonPointers: 503 - bar`, 504 "resource.customizations.ignoreDifferences.apps_Deployment": `jqPathExpressions: 505 - bar`, 506 "resource.customizations.ignoreDifferences.all": `managedFieldsManagers: 507 - kube-controller-manager 508 - argo-rollouts`, 509 "resource.customizations.ignoreResourceUpdates.iam-manager.k8s.io_Iamrole": `jsonPointers: 510 - bar`, 511 "resource.customizations.ignoreResourceUpdates.apps_Deployment": `jqPathExpressions: 512 - bar`, 513 } 514 515 _, settingsManager := fixtures(mergemaps(data, newData)) 516 517 overrides, err := settingsManager.GetResourceOverrides() 518 require.NoError(t, err) 519 assert.Len(t, overrides, 8) 520 assert.Len(t, overrides["admissionregistration.k8s.io/MutatingWebhookConfiguration"].IgnoreDifferences.JSONPointers, 1) 521 assert.Equal(t, "bar", overrides["admissionregistration.k8s.io/MutatingWebhookConfiguration"].IgnoreDifferences.JSONPointers[0]) 522 assert.Len(t, overrides["admissionregistration.k8s.io/MutatingWebhookConfiguration"].IgnoreResourceUpdates.JSONPointers, 1) 523 assert.Equal(t, "bar", overrides["admissionregistration.k8s.io/MutatingWebhookConfiguration"].IgnoreResourceUpdates.JSONPointers[0]) 524 assert.Len(t, overrides["admissionregistration.k8s.io/MutatingWebhookConfiguration"].KnownTypeFields, 1) 525 assert.Equal(t, "bar", overrides["admissionregistration.k8s.io/MutatingWebhookConfiguration"].KnownTypeFields[0].Type) 526 assert.Equal(t, "bar", overrides["admissionregistration.k8s.io/MutatingWebhookConfiguration"].HealthLua) 527 assert.Equal(t, "bar", overrides["certmanager.k8s.io/Certificate"].HealthLua) 528 assert.Equal(t, "bar", overrides["cert-manager.io/Certificate"].HealthLua) 529 assert.False(t, overrides["certmanager.k8s.io/Certificate"].UseOpenLibs) 530 assert.True(t, overrides["cert-manager.io/Certificate"].UseOpenLibs) 531 assert.Equal(t, "bar", overrides["apps/Deployment"].Actions) 532 assert.Equal(t, "bar", overrides["Deployment"].Actions) 533 assert.Equal(t, "bar", overrides["iam-manager.k8s.io/Iamrole"].HealthLua) 534 assert.Equal(t, "bar", overrides["Iamrole"].HealthLua) 535 assert.Len(t, overrides["iam-manager.k8s.io/Iamrole"].IgnoreDifferences.JSONPointers, 1) 536 assert.Len(t, overrides["apps/Deployment"].IgnoreDifferences.JQPathExpressions, 1) 537 assert.Equal(t, "bar", overrides["apps/Deployment"].IgnoreDifferences.JQPathExpressions[0]) 538 assert.Len(t, overrides["*/*"].IgnoreDifferences.ManagedFieldsManagers, 2) 539 assert.Equal(t, "kube-controller-manager", overrides["*/*"].IgnoreDifferences.ManagedFieldsManagers[0]) 540 assert.Equal(t, "argo-rollouts", overrides["*/*"].IgnoreDifferences.ManagedFieldsManagers[1]) 541 assert.Len(t, overrides["iam-manager.k8s.io/Iamrole"].IgnoreResourceUpdates.JSONPointers, 1) 542 assert.Len(t, overrides["apps/Deployment"].IgnoreResourceUpdates.JQPathExpressions, 1) 543 assert.Equal(t, "bar", overrides["apps/Deployment"].IgnoreResourceUpdates.JQPathExpressions[0]) 544 }) 545 } 546 547 func mergemaps(mapA map[string]string, mapB map[string]string) map[string]string { 548 for k, v := range mapA { 549 mapB[k] = v 550 } 551 return mapB 552 } 553 554 func TestGetIgnoreResourceUpdatesOverrides(t *testing.T) { 555 allDefault := v1alpha1.ResourceOverride{IgnoreDifferences: v1alpha1.OverrideIgnoreDiff{ 556 JSONPointers: []string{"/metadata/resourceVersion", "/metadata/generation", "/metadata/managedFields"}, 557 }} 558 allGK := "*/*" 559 560 testCustomizations := map[string]string{ 561 "resource.compareoptions": ` 562 ignoreResourceStatusField: none 563 ignoreDifferencesOnResourceUpdates: true`, 564 565 "resource.customizations": ` 566 admissionregistration.k8s.io/MutatingWebhookConfiguration: 567 ignoreDifferences: | 568 jsonPointers: 569 - /webhooks/0/clientConfig/caBundle 570 jqPathExpressions: 571 - .webhooks[0].clientConfig.caBundle 572 ignoreResourceUpdates: | 573 jsonPointers: 574 - /webhooks/1/clientConfig/caBundle 575 jqPathExpressions: 576 - .webhooks[1].clientConfig.caBundle`, 577 } 578 579 _, settingsManager := fixtures(testCustomizations) 580 overrides, err := settingsManager.GetIgnoreResourceUpdatesOverrides() 581 require.NoError(t, err) 582 583 // default overrides should always be present 584 allOverrides := overrides[allGK] 585 assert.NotNil(t, allOverrides) 586 assert.Equal(t, allDefault, allOverrides) 587 588 // with ignoreDifferencesOnResourceUpdates, ignoreDifferences should be added 589 assert.NotNil(t, overrides["admissionregistration.k8s.io/MutatingWebhookConfiguration"]) 590 assert.Equal(t, v1alpha1.ResourceOverride{ 591 IgnoreDifferences: v1alpha1.OverrideIgnoreDiff{ 592 JSONPointers: []string{"/webhooks/1/clientConfig/caBundle", "/webhooks/0/clientConfig/caBundle"}, 593 JQPathExpressions: []string{".webhooks[1].clientConfig.caBundle", ".webhooks[0].clientConfig.caBundle"}, 594 }, 595 IgnoreResourceUpdates: v1alpha1.OverrideIgnoreDiff{}, 596 }, overrides["admissionregistration.k8s.io/MutatingWebhookConfiguration"]) 597 598 // without ignoreDifferencesOnResourceUpdates, only ignoreResourceUpdates should be added 599 _, settingsManager = fixtures(mergemaps(map[string]string{ 600 "resource.compareoptions": ` 601 ignoreResourceStatusField: none 602 ignoreDifferencesOnResourceUpdates: false`, 603 }, testCustomizations)) 604 overrides, err = settingsManager.GetIgnoreResourceUpdatesOverrides() 605 require.NoError(t, err) 606 assert.NotNil(t, overrides["admissionregistration.k8s.io/MutatingWebhookConfiguration"]) 607 assert.Equal(t, v1alpha1.ResourceOverride{ 608 IgnoreDifferences: v1alpha1.OverrideIgnoreDiff{ 609 JSONPointers: []string{"/webhooks/1/clientConfig/caBundle"}, 610 JQPathExpressions: []string{".webhooks[1].clientConfig.caBundle"}, 611 }, 612 IgnoreResourceUpdates: v1alpha1.OverrideIgnoreDiff{}, 613 }, overrides["admissionregistration.k8s.io/MutatingWebhookConfiguration"]) 614 } 615 616 func TestConvertToOverrideKey(t *testing.T) { 617 key, err := convertToOverrideKey("cert-manager.io_Certificate") 618 require.NoError(t, err) 619 assert.Equal(t, "cert-manager.io/Certificate", key) 620 621 key, err = convertToOverrideKey("Certificate") 622 require.NoError(t, err) 623 assert.Equal(t, "Certificate", key) 624 625 _, err = convertToOverrideKey("") 626 require.Error(t, err) 627 628 _, err = convertToOverrideKey("_") 629 require.NoError(t, err) 630 } 631 632 func TestGetResourceCompareOptions(t *testing.T) { 633 // ignoreAggregatedRules is true 634 { 635 _, settingsManager := fixtures(map[string]string{ 636 "resource.compareoptions": "ignoreAggregatedRoles: true", 637 }) 638 compareOptions, err := settingsManager.GetResourceCompareOptions() 639 require.NoError(t, err) 640 assert.True(t, compareOptions.IgnoreAggregatedRoles) 641 } 642 643 // ignoreAggregatedRules is false 644 { 645 _, settingsManager := fixtures(map[string]string{ 646 "resource.compareoptions": "ignoreAggregatedRoles: false", 647 }) 648 compareOptions, err := settingsManager.GetResourceCompareOptions() 649 require.NoError(t, err) 650 assert.False(t, compareOptions.IgnoreAggregatedRoles) 651 } 652 653 // ignoreDifferencesOnResourceUpdates is true 654 { 655 _, settingsManager := fixtures(map[string]string{ 656 "resource.compareoptions": "ignoreDifferencesOnResourceUpdates: true", 657 }) 658 compareOptions, err := settingsManager.GetResourceCompareOptions() 659 require.NoError(t, err) 660 assert.True(t, compareOptions.IgnoreDifferencesOnResourceUpdates) 661 } 662 663 // ignoreDifferencesOnResourceUpdates is false 664 { 665 _, settingsManager := fixtures(map[string]string{ 666 "resource.compareoptions": "ignoreDifferencesOnResourceUpdates: false", 667 }) 668 compareOptions, err := settingsManager.GetResourceCompareOptions() 669 require.NoError(t, err) 670 assert.False(t, compareOptions.IgnoreDifferencesOnResourceUpdates) 671 } 672 673 // The empty resource.compareoptions should result in default being returned 674 { 675 _, settingsManager := fixtures(map[string]string{ 676 "resource.compareoptions": "", 677 }) 678 compareOptions, err := settingsManager.GetResourceCompareOptions() 679 defaultOptions := GetDefaultDiffOptions() 680 require.NoError(t, err) 681 assert.Equal(t, defaultOptions.IgnoreAggregatedRoles, compareOptions.IgnoreAggregatedRoles) 682 assert.Equal(t, defaultOptions.IgnoreDifferencesOnResourceUpdates, compareOptions.IgnoreDifferencesOnResourceUpdates) 683 } 684 685 // resource.compareoptions not defined - should result in default being returned 686 { 687 _, settingsManager := fixtures(map[string]string{}) 688 compareOptions, err := settingsManager.GetResourceCompareOptions() 689 defaultOptions := GetDefaultDiffOptions() 690 require.NoError(t, err) 691 assert.Equal(t, defaultOptions.IgnoreAggregatedRoles, compareOptions.IgnoreAggregatedRoles) 692 assert.Equal(t, defaultOptions.IgnoreDifferencesOnResourceUpdates, compareOptions.IgnoreDifferencesOnResourceUpdates) 693 } 694 } 695 696 func TestSettingsManager_GetKustomizeBuildOptions(t *testing.T) { 697 t.Run("Empty", func(t *testing.T) { 698 _, settingsManager := fixtures(map[string]string{}) 699 700 settings, err := settingsManager.GetKustomizeSettings() 701 702 require.NoError(t, err) 703 assert.Empty(t, settings.BuildOptions) 704 assert.Empty(t, settings.Versions) 705 }) 706 t.Run("Set", func(t *testing.T) { 707 _, settingsManager := fixtures(map[string]string{ 708 "kustomize.buildOptions": "foo", 709 "kustomize.version.v3.2.1": "somePath", 710 }) 711 712 options, err := settingsManager.GetKustomizeSettings() 713 714 require.NoError(t, err) 715 assert.Equal(t, "foo", options.BuildOptions) 716 assert.Equal(t, []v1alpha1.KustomizeVersion{{Name: "v3.2.1", Path: "somePath"}}, options.Versions) 717 }) 718 719 t.Run("Kustomize settings per-version", func(t *testing.T) { 720 _, settingsManager := fixtures(map[string]string{ 721 "kustomize.buildOptions": "--global true", 722 "kustomize.version.v3.2.1": "/path_3.2.1", 723 "kustomize.buildOptions.v3.2.3": "--options v3.2.3", 724 "kustomize.path.v3.2.3": "/path_3.2.3", 725 "kustomize.path.v3.2.4": "/path_3.2.4", 726 "kustomize.buildOptions.v3.2.4": "--options v3.2.4", 727 "kustomize.buildOptions.v3.2.5": "--options v3.2.5", 728 }) 729 730 got, err := settingsManager.GetKustomizeSettings() 731 732 require.NoError(t, err) 733 assert.Equal(t, "--global true", got.BuildOptions) 734 want := &v1alpha1.KustomizeOptions{ 735 BuildOptions: "--global true", 736 Versions: []v1alpha1.KustomizeVersion{ 737 {Name: "v3.2.1", Path: "/path_3.2.1"}, 738 {Name: "v3.2.3", Path: "/path_3.2.3", BuildOptions: "--options v3.2.3"}, 739 {Name: "v3.2.4", Path: "/path_3.2.4", BuildOptions: "--options v3.2.4"}, 740 }, 741 } 742 sortVersionsByName := func(versions []v1alpha1.KustomizeVersion) { 743 sort.Slice(versions, func(i, j int) bool { 744 return versions[i].Name > versions[j].Name 745 }) 746 } 747 sortVersionsByName(want.Versions) 748 sortVersionsByName(got.Versions) 749 assert.Equal(t, want, got) 750 }) 751 752 t.Run("Kustomize settings per-version with duplicate versions", func(t *testing.T) { 753 _, settingsManager := fixtures(map[string]string{ 754 "kustomize.buildOptions": "--global true", 755 "kustomize.version.v3.2.1": "/path_3.2.1", 756 "kustomize.buildOptions.v3.2.1": "--options v3.2.3", 757 "kustomize.path.v3.2.2": "/other_path_3.2.2", 758 "kustomize.path.v3.2.1": "/other_path_3.2.1", 759 }) 760 761 got, err := settingsManager.GetKustomizeSettings() 762 require.ErrorContains(t, err, "found duplicate kustomize version: v3.2.1") 763 assert.Empty(t, got) 764 }) 765 766 t.Run("Config map with no Kustomize settings", func(t *testing.T) { 767 _, settingsManager := fixtures(map[string]string{ 768 "other.options": "--global true", 769 }) 770 771 got, err := settingsManager.GetKustomizeSettings() 772 require.NoError(t, err) 773 assert.Empty(t, got) 774 }) 775 } 776 777 func TestSettingsManager_GetEventLabelKeys(t *testing.T) { 778 tests := []struct { 779 name string 780 data string 781 expectedKeys []string 782 }{ 783 { 784 name: "Comma separated data", 785 data: "app,env, tier, example.com/team-*, *", 786 expectedKeys: []string{"app", "env", "tier", "example.com/team-*", "*"}, 787 }, 788 { 789 name: "Empty data", 790 expectedKeys: []string{}, 791 }, 792 } 793 for _, tt := range tests { 794 t.Run(tt.name, func(t *testing.T) { 795 _, settingsManager := fixtures(map[string]string{}) 796 if tt.data != "" { 797 _, settingsManager = fixtures(map[string]string{ 798 resourceIncludeEventLabelKeys: tt.data, 799 resourceExcludeEventLabelKeys: tt.data, 800 }) 801 } 802 803 inKeys := settingsManager.GetIncludeEventLabelKeys() 804 assert.Len(t, inKeys, len(tt.expectedKeys)) 805 806 exKeys := settingsManager.GetExcludeEventLabelKeys() 807 assert.Len(t, exKeys, len(tt.expectedKeys)) 808 809 for i := range tt.expectedKeys { 810 assert.Equal(t, tt.expectedKeys[i], inKeys[i]) 811 assert.Equal(t, tt.expectedKeys[i], exKeys[i]) 812 } 813 }) 814 } 815 } 816 817 func Test_GetKustomizeBinaryPath(t *testing.T) { 818 ko := &v1alpha1.KustomizeOptions{ 819 BuildOptions: "--opt1 val1", 820 Versions: []v1alpha1.KustomizeVersion{ 821 {Name: "v1", Path: "path_v1"}, 822 {Name: "v2", Path: "path_v2"}, 823 {Name: "v3", Path: "path_v3", BuildOptions: "--opt2 val2"}, 824 }, 825 } 826 827 t.Run("VersionDoesNotExist", func(t *testing.T) { 828 _, err := GetKustomizeBinaryPath(ko, v1alpha1.ApplicationSource{ 829 Kustomize: &v1alpha1.ApplicationSourceKustomize{Version: "v4"}, 830 }) 831 require.Error(t, err) 832 }) 833 834 t.Run("DefaultBuildOptions", func(t *testing.T) { 835 ver, err := GetKustomizeBinaryPath(ko, v1alpha1.ApplicationSource{}) 836 require.NoError(t, err) 837 assert.Empty(t, ver) 838 }) 839 840 t.Run("VersionExists", func(t *testing.T) { 841 ver, err := GetKustomizeBinaryPath(ko, v1alpha1.ApplicationSource{ 842 Kustomize: &v1alpha1.ApplicationSourceKustomize{Version: "v2"}, 843 }) 844 require.NoError(t, err) 845 assert.Equal(t, "path_v2", ver) 846 }) 847 848 t.Run("VersionExistsWithBuildOption", func(t *testing.T) { 849 ver, err := GetKustomizeBinaryPath(ko, v1alpha1.ApplicationSource{ 850 Kustomize: &v1alpha1.ApplicationSourceKustomize{Version: "v3"}, 851 }) 852 require.NoError(t, err) 853 assert.Equal(t, "path_v3", ver) 854 }) 855 856 t.Run("ExplicitVersionSet", func(t *testing.T) { 857 // nolint:staticcheck // test for backwards compatibility with deprecated field 858 ko.BinaryPath = "custom_path" 859 ver, err := GetKustomizeBinaryPath(ko, v1alpha1.ApplicationSource{ 860 Kustomize: &v1alpha1.ApplicationSourceKustomize{Version: "v3"}, 861 }) 862 require.NoError(t, err) 863 assert.Equal(t, "custom_path", ver) 864 }) 865 } 866 867 func TestGetGoogleAnalytics(t *testing.T) { 868 _, settingsManager := fixtures(map[string]string{ 869 "ga.trackingid": "123", 870 }) 871 ga, err := settingsManager.GetGoogleAnalytics() 872 require.NoError(t, err) 873 assert.Equal(t, "123", ga.TrackingID) 874 assert.True(t, ga.AnonymizeUsers) 875 } 876 877 func TestSettingsManager_GetHelp(t *testing.T) { 878 t.Run("Default", func(t *testing.T) { 879 _, settingsManager := fixtures(nil) 880 h, err := settingsManager.GetHelp() 881 require.NoError(t, err) 882 assert.Empty(t, h.ChatURL) 883 assert.Empty(t, h.ChatText) 884 }) 885 t.Run("Set", func(t *testing.T) { 886 _, settingsManager := fixtures(map[string]string{ 887 "help.chatUrl": "foo", 888 "help.chatText": "bar", 889 }) 890 h, err := settingsManager.GetHelp() 891 require.NoError(t, err) 892 assert.Equal(t, "foo", h.ChatURL) 893 assert.Equal(t, "bar", h.ChatText) 894 }) 895 t.Run("SetOnlyChatUrl", func(t *testing.T) { 896 _, settingManager := fixtures(map[string]string{ 897 "help.chatUrl": "foo", 898 }) 899 h, err := settingManager.GetHelp() 900 require.NoError(t, err) 901 assert.Equal(t, "foo", h.ChatURL) 902 assert.Equal(t, "Chat now!", h.ChatText) 903 }) 904 t.Run("SetOnlyChatText", func(t *testing.T) { 905 _, settingManager := fixtures(map[string]string{ 906 "help.chatText": "bar", 907 }) 908 h, err := settingManager.GetHelp() 909 require.NoError(t, err) 910 assert.Empty(t, h.ChatURL) 911 assert.Empty(t, h.ChatText) 912 }) 913 t.Run("GetBinaryUrls", func(t *testing.T) { 914 _, settingsManager := fixtures(map[string]string{ 915 "help.download.darwin-amd64": "amd64-path", 916 "help.download.linux-s390x": "s390x-path", 917 "help.download.unsupported": "nowhere", 918 }) 919 h, err := settingsManager.GetHelp() 920 require.NoError(t, err) 921 assert.Equal(t, map[string]string{"darwin-amd64": "amd64-path", "linux-s390x": "s390x-path"}, h.BinaryURLs) 922 }) 923 } 924 925 func TestSettingsManager_GetSettings(t *testing.T) { 926 t.Run("UserSessionDurationNotProvided", func(t *testing.T) { 927 kubeClient := fake.NewClientset( 928 &corev1.ConfigMap{ 929 ObjectMeta: metav1.ObjectMeta{ 930 Name: common.ArgoCDConfigMapName, 931 Namespace: "default", 932 Labels: map[string]string{ 933 "app.kubernetes.io/part-of": "argocd", 934 }, 935 }, 936 Data: nil, 937 }, 938 &corev1.Secret{ 939 ObjectMeta: metav1.ObjectMeta{ 940 Name: common.ArgoCDSecretName, 941 Namespace: "default", 942 Labels: map[string]string{ 943 "app.kubernetes.io/part-of": "argocd", 944 }, 945 }, 946 Data: map[string][]byte{ 947 "server.secretkey": nil, 948 }, 949 }, 950 ) 951 settingsManager := NewSettingsManager(t.Context(), kubeClient, "default") 952 s, err := settingsManager.GetSettings() 953 require.NoError(t, err) 954 assert.Equal(t, time.Hour*24, s.UserSessionDuration) 955 }) 956 t.Run("UserSessionDurationInvalidFormat", func(t *testing.T) { 957 kubeClient := fake.NewClientset( 958 &corev1.ConfigMap{ 959 ObjectMeta: metav1.ObjectMeta{ 960 Name: common.ArgoCDConfigMapName, 961 Namespace: "default", 962 Labels: map[string]string{ 963 "app.kubernetes.io/part-of": "argocd", 964 }, 965 }, 966 Data: map[string]string{ 967 "users.session.duration": "10hh", 968 }, 969 }, 970 &corev1.Secret{ 971 ObjectMeta: metav1.ObjectMeta{ 972 Name: common.ArgoCDSecretName, 973 Namespace: "default", 974 Labels: map[string]string{ 975 "app.kubernetes.io/part-of": "argocd", 976 }, 977 }, 978 Data: map[string][]byte{ 979 "server.secretkey": nil, 980 }, 981 }, 982 ) 983 settingsManager := NewSettingsManager(t.Context(), kubeClient, "default") 984 s, err := settingsManager.GetSettings() 985 require.NoError(t, err) 986 assert.Equal(t, time.Hour*24, s.UserSessionDuration) 987 }) 988 t.Run("UserSessionDurationProvided", func(t *testing.T) { 989 kubeClient := fake.NewClientset( 990 &corev1.ConfigMap{ 991 ObjectMeta: metav1.ObjectMeta{ 992 Name: common.ArgoCDConfigMapName, 993 Namespace: "default", 994 Labels: map[string]string{ 995 "app.kubernetes.io/part-of": "argocd", 996 }, 997 }, 998 Data: map[string]string{ 999 "users.session.duration": "10h", 1000 }, 1001 }, 1002 &corev1.Secret{ 1003 ObjectMeta: metav1.ObjectMeta{ 1004 Name: common.ArgoCDSecretName, 1005 Namespace: "default", 1006 Labels: map[string]string{ 1007 "app.kubernetes.io/part-of": "argocd", 1008 }, 1009 }, 1010 Data: map[string][]byte{ 1011 "server.secretkey": nil, 1012 }, 1013 }, 1014 ) 1015 settingsManager := NewSettingsManager(t.Context(), kubeClient, "default") 1016 s, err := settingsManager.GetSettings() 1017 require.NoError(t, err) 1018 assert.Equal(t, time.Hour*10, s.UserSessionDuration) 1019 }) 1020 } 1021 1022 func TestGetOIDCConfig(t *testing.T) { 1023 kubeClient := fake.NewClientset( 1024 &corev1.ConfigMap{ 1025 ObjectMeta: metav1.ObjectMeta{ 1026 Name: common.ArgoCDConfigMapName, 1027 Namespace: "default", 1028 Labels: map[string]string{ 1029 "app.kubernetes.io/part-of": "argocd", 1030 }, 1031 }, 1032 Data: map[string]string{ 1033 "oidc.config": "\n requestedIDTokenClaims: {\"groups\": {\"essential\": true}}\n", 1034 }, 1035 }, 1036 &corev1.Secret{ 1037 ObjectMeta: metav1.ObjectMeta{ 1038 Name: common.ArgoCDSecretName, 1039 Namespace: "default", 1040 Labels: map[string]string{ 1041 "app.kubernetes.io/part-of": "argocd", 1042 }, 1043 }, 1044 Data: map[string][]byte{ 1045 "admin.password": nil, 1046 "server.secretkey": nil, 1047 }, 1048 }, 1049 ) 1050 settingsManager := NewSettingsManager(t.Context(), kubeClient, "default") 1051 settings, err := settingsManager.GetSettings() 1052 require.NoError(t, err) 1053 1054 oidcConfig := settings.OIDCConfig() 1055 assert.NotNil(t, oidcConfig) 1056 1057 claim := oidcConfig.RequestedIDTokenClaims["groups"] 1058 assert.NotNil(t, claim) 1059 assert.True(t, claim.Essential) 1060 } 1061 1062 func TestRedirectURL(t *testing.T) { 1063 cases := map[string][]string{ 1064 "https://localhost:4000": {"https://localhost:4000/auth/callback", "https://localhost:4000/api/dex/callback"}, 1065 "https://localhost:4000/": {"https://localhost:4000/auth/callback", "https://localhost:4000/api/dex/callback"}, 1066 "https://localhost:4000/argocd": {"https://localhost:4000/argocd/auth/callback", "https://localhost:4000/argocd/api/dex/callback"}, 1067 "https://localhost:4000/argocd/": {"https://localhost:4000/argocd/auth/callback", "https://localhost:4000/argocd/api/dex/callback"}, 1068 } 1069 for given, expected := range cases { 1070 settings := ArgoCDSettings{URL: given} 1071 redirectURL, err := settings.RedirectURL() 1072 require.NoError(t, err) 1073 assert.Equal(t, expected[0], redirectURL) 1074 dexRedirectURL, err := settings.DexRedirectURL() 1075 require.NoError(t, err) 1076 assert.Equal(t, expected[1], dexRedirectURL) 1077 } 1078 } 1079 1080 func Test_validateExternalURL(t *testing.T) { 1081 tests := []struct { 1082 name string 1083 url string 1084 errMsg string 1085 }{ 1086 {name: "Valid URL", url: "https://my.domain.com"}, 1087 {name: "No URL - Valid", url: ""}, 1088 {name: "Invalid URL", url: "my.domain.com", errMsg: "URL must include http or https protocol"}, 1089 } 1090 for _, tt := range tests { 1091 t.Run(tt.name, func(t *testing.T) { 1092 err := validateExternalURL(tt.url) 1093 if tt.errMsg != "" { 1094 assert.EqualError(t, err, tt.errMsg) 1095 } else { 1096 require.NoError(t, err) 1097 } 1098 }) 1099 } 1100 } 1101 1102 func TestGetOIDCSecretTrim(t *testing.T) { 1103 kubeClient := fake.NewClientset( 1104 &corev1.ConfigMap{ 1105 ObjectMeta: metav1.ObjectMeta{ 1106 Name: common.ArgoCDConfigMapName, 1107 Namespace: "default", 1108 Labels: map[string]string{ 1109 "app.kubernetes.io/part-of": "argocd", 1110 }, 1111 }, 1112 Data: map[string]string{ 1113 "oidc.config": "\n name: Okta\n clientSecret: test-secret\r\n \n clientID: aaaabbbbccccddddeee\n", 1114 }, 1115 }, 1116 &corev1.Secret{ 1117 ObjectMeta: metav1.ObjectMeta{ 1118 Name: common.ArgoCDSecretName, 1119 Namespace: "default", 1120 Labels: map[string]string{ 1121 "app.kubernetes.io/part-of": "argocd", 1122 }, 1123 }, 1124 Data: map[string][]byte{ 1125 "admin.password": nil, 1126 "server.secretkey": nil, 1127 }, 1128 }, 1129 ) 1130 settingsManager := NewSettingsManager(t.Context(), kubeClient, "default") 1131 settings, err := settingsManager.GetSettings() 1132 require.NoError(t, err) 1133 1134 oidcConfig := settings.OIDCConfig() 1135 assert.NotNil(t, oidcConfig) 1136 assert.Equal(t, "test-secret", oidcConfig.ClientSecret) 1137 } 1138 1139 func getCNFromCertificate(cert *tls.Certificate) string { 1140 c, err := x509.ParseCertificate(cert.Certificate[0]) 1141 if err != nil { 1142 return "" 1143 } 1144 return c.Subject.CommonName 1145 } 1146 1147 func Test_GetTLSConfiguration(t *testing.T) { 1148 t.Run("Valid external TLS secret with success", func(t *testing.T) { 1149 kubeClient := fake.NewClientset( 1150 &corev1.ConfigMap{ 1151 ObjectMeta: metav1.ObjectMeta{ 1152 Name: common.ArgoCDConfigMapName, 1153 Namespace: "default", 1154 Labels: map[string]string{ 1155 "app.kubernetes.io/part-of": "argocd", 1156 }, 1157 }, 1158 Data: map[string]string{ 1159 "oidc.config": "\n name: Okta\n clientSecret: test-secret\r\n \n clientID: aaaabbbbccccddddeee\n", 1160 }, 1161 }, 1162 &corev1.Secret{ 1163 ObjectMeta: metav1.ObjectMeta{ 1164 Name: common.ArgoCDSecretName, 1165 Namespace: "default", 1166 Labels: map[string]string{ 1167 "app.kubernetes.io/part-of": "argocd", 1168 }, 1169 }, 1170 Data: map[string][]byte{ 1171 "admin.password": nil, 1172 "server.secretkey": nil, 1173 }, 1174 }, 1175 &corev1.Secret{ 1176 ObjectMeta: metav1.ObjectMeta{ 1177 Name: externalServerTLSSecretName, 1178 Namespace: "default", 1179 }, 1180 Data: map[string][]byte{ 1181 "tls.crt": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-test-server.crt")), 1182 "tls.key": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-test-server.key")), 1183 }, 1184 }, 1185 ) 1186 settingsManager := NewSettingsManager(t.Context(), kubeClient, "default") 1187 settings, err := settingsManager.GetSettings() 1188 require.NoError(t, err) 1189 assert.True(t, settings.CertificateIsExternal) 1190 assert.NotNil(t, settings.Certificate) 1191 assert.Contains(t, getCNFromCertificate(settings.Certificate), "localhost") 1192 }) 1193 1194 t.Run("Valid external TLS secret overrides argocd-secret", func(t *testing.T) { 1195 kubeClient := fake.NewClientset( 1196 &corev1.ConfigMap{ 1197 ObjectMeta: metav1.ObjectMeta{ 1198 Name: common.ArgoCDConfigMapName, 1199 Namespace: "default", 1200 Labels: map[string]string{ 1201 "app.kubernetes.io/part-of": "argocd", 1202 }, 1203 }, 1204 Data: map[string]string{ 1205 "oidc.config": "\n name: Okta\n clientSecret: test-secret\r\n \n clientID: aaaabbbbccccddddeee\n", 1206 }, 1207 }, 1208 &corev1.Secret{ 1209 ObjectMeta: metav1.ObjectMeta{ 1210 Name: common.ArgoCDSecretName, 1211 Namespace: "default", 1212 Labels: map[string]string{ 1213 "app.kubernetes.io/part-of": "argocd", 1214 }, 1215 }, 1216 Data: map[string][]byte{ 1217 "admin.password": nil, 1218 "server.secretkey": nil, 1219 "tls.crt": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-e2e-server.crt")), 1220 "tls.key": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-e2e-server.key")), 1221 }, 1222 }, 1223 &corev1.Secret{ 1224 ObjectMeta: metav1.ObjectMeta{ 1225 Name: externalServerTLSSecretName, 1226 Namespace: "default", 1227 }, 1228 Data: map[string][]byte{ 1229 "tls.crt": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-test-server.crt")), 1230 "tls.key": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-test-server.key")), 1231 }, 1232 }, 1233 ) 1234 settingsManager := NewSettingsManager(t.Context(), kubeClient, "default") 1235 settings, err := settingsManager.GetSettings() 1236 require.NoError(t, err) 1237 assert.True(t, settings.CertificateIsExternal) 1238 assert.NotNil(t, settings.Certificate) 1239 assert.Contains(t, getCNFromCertificate(settings.Certificate), "localhost") 1240 }) 1241 t.Run("Invalid external TLS secret", func(t *testing.T) { 1242 kubeClient := fake.NewClientset( 1243 &corev1.ConfigMap{ 1244 ObjectMeta: metav1.ObjectMeta{ 1245 Name: common.ArgoCDConfigMapName, 1246 Namespace: "default", 1247 Labels: map[string]string{ 1248 "app.kubernetes.io/part-of": "argocd", 1249 }, 1250 }, 1251 Data: map[string]string{ 1252 "oidc.config": "\n name: Okta\n clientSecret: test-secret\r\n \n clientID: aaaabbbbccccddddeee\n", 1253 }, 1254 }, 1255 &corev1.Secret{ 1256 ObjectMeta: metav1.ObjectMeta{ 1257 Name: common.ArgoCDSecretName, 1258 Namespace: "default", 1259 Labels: map[string]string{ 1260 "app.kubernetes.io/part-of": "argocd", 1261 }, 1262 }, 1263 Data: map[string][]byte{ 1264 "admin.password": nil, 1265 "server.secretkey": nil, 1266 }, 1267 }, 1268 &corev1.Secret{ 1269 ObjectMeta: metav1.ObjectMeta{ 1270 Name: externalServerTLSSecretName, 1271 Namespace: "default", 1272 }, 1273 Data: map[string][]byte{ 1274 "tls.crt": []byte(""), 1275 "tls.key": []byte(""), 1276 }, 1277 }, 1278 ) 1279 settingsManager := NewSettingsManager(t.Context(), kubeClient, "default") 1280 settings, err := settingsManager.GetSettings() 1281 require.ErrorContains(t, err, "failed to find any PEM data in certificate input") 1282 assert.NotNil(t, settings) 1283 }) 1284 t.Run("Does not parse TLS cert key pair on cache hit", func(t *testing.T) { 1285 cm := &corev1.ConfigMap{ 1286 ObjectMeta: metav1.ObjectMeta{ 1287 Name: common.ArgoCDConfigMapName, 1288 Namespace: "default", 1289 Labels: map[string]string{ 1290 "app.kubernetes.io/part-of": "argocd", 1291 }, 1292 }, 1293 } 1294 secret := &corev1.Secret{ 1295 ObjectMeta: metav1.ObjectMeta{ 1296 Name: common.ArgoCDSecretName, 1297 Namespace: "default", 1298 ResourceVersion: "1", 1299 Labels: map[string]string{ 1300 "app.kubernetes.io/part-of": "argocd", 1301 }, 1302 }, 1303 Data: map[string][]byte{ 1304 "server.secretkey": nil, 1305 }, 1306 } 1307 tlsSecret := &corev1.Secret{ 1308 ObjectMeta: metav1.ObjectMeta{ 1309 Name: externalServerTLSSecretName, 1310 Namespace: "default", 1311 ResourceVersion: "1", 1312 }, 1313 Data: map[string][]byte{ 1314 "tls.crt": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-test-server.crt")), 1315 "tls.key": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-test-server.key")), 1316 }, 1317 } 1318 1319 kubeClient := fake.NewClientset(cm, secret, tlsSecret) 1320 callCount := 0 1321 settingsManager := NewSettingsManager(context.Background(), kubeClient, "default", func(mgr *SettingsManager) { 1322 mgr.tlsCertParser = func(certpem []byte, keypem []byte) (tls.Certificate, error) { 1323 callCount++ 1324 1325 return tls.X509KeyPair(certpem, keypem) 1326 } 1327 }) 1328 1329 // should not be called by initialization 1330 assert.Equal(t, 0, callCount) 1331 1332 // should be called by first call to GetSettings 1333 _, err := settingsManager.GetSettings() 1334 require.NoError(t, err) 1335 assert.Equal(t, 1, callCount) 1336 1337 // should not be called by subsequent call to GetSettings 1338 _, err = settingsManager.GetSettings() 1339 require.NoError(t, err) 1340 assert.Equal(t, 1, callCount) 1341 }) 1342 t.Run("Parses TLS cert key pair when TLS secret update causes cache miss", func(t *testing.T) { 1343 cm := &corev1.ConfigMap{ 1344 ObjectMeta: metav1.ObjectMeta{ 1345 Name: common.ArgoCDConfigMapName, 1346 Namespace: "default", 1347 Labels: map[string]string{ 1348 "app.kubernetes.io/part-of": "argocd", 1349 }, 1350 }, 1351 } 1352 secret := &corev1.Secret{ 1353 ObjectMeta: metav1.ObjectMeta{ 1354 Name: common.ArgoCDSecretName, 1355 Namespace: "default", 1356 ResourceVersion: "1", 1357 Labels: map[string]string{ 1358 "app.kubernetes.io/part-of": "argocd", 1359 }, 1360 }, 1361 Data: map[string][]byte{ 1362 "server.secretkey": nil, 1363 }, 1364 } 1365 tlsSecret := &corev1.Secret{ 1366 ObjectMeta: metav1.ObjectMeta{ 1367 Name: externalServerTLSSecretName, 1368 Namespace: "default", 1369 ResourceVersion: "1", 1370 }, 1371 Data: map[string][]byte{ 1372 "tls.crt": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-test-server.crt")), 1373 "tls.key": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-test-server.key")), 1374 }, 1375 } 1376 1377 kubeClient := fake.NewClientset(cm, secret, tlsSecret) 1378 callCount := 0 1379 settingsManager := NewSettingsManager(context.Background(), kubeClient, "default", func(mgr *SettingsManager) { 1380 mgr.tlsCertParser = func(certpem []byte, keypem []byte) (tls.Certificate, error) { 1381 callCount++ 1382 1383 return tls.X509KeyPair(certpem, keypem) 1384 } 1385 }) 1386 1387 // should be called by first call to GetSettings 1388 settings, err := settingsManager.GetSettings() 1389 require.NoError(t, err) 1390 assert.Equal(t, 1, callCount) 1391 assert.NotNil(t, settings.Certificate) 1392 assert.True(t, settings.CertificateIsExternal) 1393 assert.Equal(t, "localhost", getCNFromCertificate(settings.Certificate)) 1394 1395 // update secret 1396 tlsSecret.SetResourceVersion("2") 1397 _, err = kubeClient.CoreV1().Secrets("default").Update(t.Context(), tlsSecret, metav1.UpdateOptions{}) 1398 require.NoError(t, err) 1399 1400 // allow time for the udpate to resolve to avoid timing issues below 1401 time.Sleep(250 * time.Millisecond) 1402 1403 // should be called again after secret update resolves 1404 settings, err = settingsManager.GetSettings() 1405 require.NoError(t, err) 1406 assert.Equal(t, 2, callCount) 1407 assert.NotNil(t, settings.Certificate) 1408 assert.True(t, settings.CertificateIsExternal) 1409 assert.Equal(t, "localhost", getCNFromCertificate(settings.Certificate)) 1410 }) 1411 t.Run("Overrides cached internal TLS cert when external TLS secret added", func(t *testing.T) { 1412 kubeClient := fake.NewClientset( 1413 &corev1.ConfigMap{ 1414 ObjectMeta: metav1.ObjectMeta{ 1415 Name: common.ArgoCDConfigMapName, 1416 Namespace: "default", 1417 Labels: map[string]string{ 1418 "app.kubernetes.io/part-of": "argocd", 1419 }, 1420 }, 1421 }, 1422 &corev1.Secret{ 1423 ObjectMeta: metav1.ObjectMeta{ 1424 Name: common.ArgoCDSecretName, 1425 Namespace: "default", 1426 ResourceVersion: "1", 1427 Labels: map[string]string{ 1428 "app.kubernetes.io/part-of": "argocd", 1429 }, 1430 }, 1431 Data: map[string][]byte{ 1432 "server.secretkey": nil, 1433 "tls.crt": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-e2e-server.crt")), 1434 "tls.key": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-e2e-server.key")), 1435 }, 1436 }, 1437 ) 1438 settingsManager := NewSettingsManager(t.Context(), kubeClient, "default") 1439 settings, err := settingsManager.GetSettings() 1440 require.NoError(t, err) 1441 // should have internal cert at this point 1442 assert.NotNil(t, settings.Certificate) 1443 assert.False(t, settings.CertificateIsExternal) 1444 assert.Equal(t, "Argo CD E2E", getCNFromCertificate(settings.Certificate)) 1445 1446 externalTLSSecret := &corev1.Secret{ 1447 ObjectMeta: metav1.ObjectMeta{ 1448 Name: externalServerTLSSecretName, 1449 Namespace: "default", 1450 ResourceVersion: "1", 1451 }, 1452 Data: map[string][]byte{ 1453 "tls.crt": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-test-server.crt")), 1454 "tls.key": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-test-server.key")), 1455 }, 1456 } 1457 _, err = kubeClient.CoreV1().Secrets("default").Create(t.Context(), externalTLSSecret, metav1.CreateOptions{}) 1458 require.NoError(t, err) 1459 1460 // allow time for the create to resolve to avoid timing issues below 1461 time.Sleep(250 * time.Millisecond) 1462 1463 settings, err = settingsManager.GetSettings() 1464 require.NoError(t, err) 1465 // should now have an external cert 1466 assert.NotNil(t, settings.Certificate) 1467 assert.True(t, settings.CertificateIsExternal) 1468 assert.Equal(t, "localhost", getCNFromCertificate(settings.Certificate)) 1469 }) 1470 t.Run("Falls back to internal TLS cert when external TLS secret deleted", func(t *testing.T) { 1471 kubeClient := fake.NewClientset( 1472 &corev1.ConfigMap{ 1473 ObjectMeta: metav1.ObjectMeta{ 1474 Name: common.ArgoCDConfigMapName, 1475 Namespace: "default", 1476 Labels: map[string]string{ 1477 "app.kubernetes.io/part-of": "argocd", 1478 }, 1479 }, 1480 }, 1481 &corev1.Secret{ 1482 ObjectMeta: metav1.ObjectMeta{ 1483 Name: common.ArgoCDSecretName, 1484 Namespace: "default", 1485 ResourceVersion: "1", 1486 Labels: map[string]string{ 1487 "app.kubernetes.io/part-of": "argocd", 1488 }, 1489 }, 1490 Data: map[string][]byte{ 1491 "server.secretkey": nil, 1492 "tls.crt": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-e2e-server.crt")), 1493 "tls.key": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-e2e-server.key")), 1494 }, 1495 }, 1496 &corev1.Secret{ 1497 ObjectMeta: metav1.ObjectMeta{ 1498 Name: externalServerTLSSecretName, 1499 Namespace: "default", 1500 ResourceVersion: "1", 1501 }, 1502 Data: map[string][]byte{ 1503 "tls.crt": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-test-server.crt")), 1504 "tls.key": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-test-server.key")), 1505 }, 1506 }, 1507 ) 1508 settingsManager := NewSettingsManager(t.Context(), kubeClient, "default") 1509 settings, err := settingsManager.GetSettings() 1510 require.NoError(t, err) 1511 // should have external cert at this point 1512 assert.NotNil(t, settings.Certificate) 1513 assert.True(t, settings.CertificateIsExternal) 1514 assert.Equal(t, "localhost", getCNFromCertificate(settings.Certificate)) 1515 1516 err = kubeClient.CoreV1().Secrets("default").Delete(t.Context(), externalServerTLSSecretName, metav1.DeleteOptions{}) 1517 require.NoError(t, err) 1518 1519 // allow time for the delete to resolve to avoid timing issues below 1520 time.Sleep(250 * time.Millisecond) 1521 1522 settings, err = settingsManager.GetSettings() 1523 require.NoError(t, err) 1524 // should now have an internal cert 1525 assert.NotNil(t, settings.Certificate) 1526 assert.False(t, settings.CertificateIsExternal) 1527 assert.Equal(t, "Argo CD E2E", getCNFromCertificate(settings.Certificate)) 1528 }) 1529 t.Run("No external TLS secret", func(t *testing.T) { 1530 kubeClient := fake.NewClientset( 1531 &corev1.ConfigMap{ 1532 ObjectMeta: metav1.ObjectMeta{ 1533 Name: common.ArgoCDConfigMapName, 1534 Namespace: "default", 1535 Labels: map[string]string{ 1536 "app.kubernetes.io/part-of": "argocd", 1537 }, 1538 }, 1539 Data: map[string]string{ 1540 "oidc.config": "\n name: Okta\n clientSecret: test-secret\r\n \n clientID: aaaabbbbccccddddeee\n", 1541 }, 1542 }, 1543 &corev1.Secret{ 1544 ObjectMeta: metav1.ObjectMeta{ 1545 Name: common.ArgoCDSecretName, 1546 Namespace: "default", 1547 Labels: map[string]string{ 1548 "app.kubernetes.io/part-of": "argocd", 1549 }, 1550 }, 1551 Data: map[string][]byte{ 1552 "admin.password": nil, 1553 "server.secretkey": nil, 1554 "tls.crt": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-e2e-server.crt")), 1555 "tls.key": []byte(testutil.MustLoadFileToString("../../test/fixture/certs/argocd-e2e-server.key")), 1556 }, 1557 }, 1558 ) 1559 settingsManager := NewSettingsManager(t.Context(), kubeClient, "default") 1560 settings, err := settingsManager.GetSettings() 1561 require.NoError(t, err) 1562 assert.False(t, settings.CertificateIsExternal) 1563 assert.NotNil(t, settings.Certificate) 1564 assert.Contains(t, getCNFromCertificate(settings.Certificate), "Argo CD E2E") 1565 }) 1566 } 1567 1568 func TestDownloadArgoCDBinaryUrls(t *testing.T) { 1569 _, settingsManager := fixtures(map[string]string{ 1570 "help.download.darwin-amd64": "some-url", 1571 }) 1572 argoCDCM, err := settingsManager.getConfigMap() 1573 require.NoError(t, err) 1574 assert.Equal(t, "some-url", argoCDCM.Data["help.download.darwin-amd64"]) 1575 1576 _, settingsManager = fixtures(map[string]string{ 1577 "help.download.linux-s390x": "some-url", 1578 }) 1579 argoCDCM, err = settingsManager.getConfigMap() 1580 require.NoError(t, err) 1581 assert.Equal(t, "some-url", argoCDCM.Data["help.download.linux-s390x"]) 1582 1583 _, settingsManager = fixtures(map[string]string{ 1584 "help.download.unsupported": "some-url", 1585 }) 1586 argoCDCM, err = settingsManager.getConfigMap() 1587 require.NoError(t, err) 1588 assert.Equal(t, "some-url", argoCDCM.Data["help.download.unsupported"]) 1589 } 1590 1591 func TestSecretKeyRef(t *testing.T) { 1592 data := map[string]string{ 1593 "oidc.config": `name: Okta 1594 issuer: $ext:issuerSecret 1595 clientID: aaaabbbbccccddddeee 1596 clientSecret: $ext:clientSecret 1597 # Optional set of OIDC scopes to request. If omitted, defaults to: ["openid", "profile", "email", "groups"] 1598 requestedScopes: ["openid", "profile", "email"] 1599 # Optional set of OIDC claims to request on the ID token. 1600 requestedIDTokenClaims: {"groups": {"essential": true}}`, 1601 } 1602 cm := &corev1.ConfigMap{ 1603 ObjectMeta: metav1.ObjectMeta{ 1604 Name: common.ArgoCDConfigMapName, 1605 Namespace: "default", 1606 Labels: map[string]string{ 1607 "app.kubernetes.io/part-of": "argocd", 1608 }, 1609 }, 1610 Data: data, 1611 } 1612 argocdSecret := &corev1.Secret{ 1613 ObjectMeta: metav1.ObjectMeta{ 1614 Name: common.ArgoCDSecretName, 1615 Namespace: "default", 1616 }, 1617 Data: map[string][]byte{ 1618 "admin.password": nil, 1619 "server.secretkey": nil, 1620 "webhook.github.secret": []byte("$ext:webhook.github.secret"), 1621 }, 1622 } 1623 secret := &corev1.Secret{ 1624 ObjectMeta: metav1.ObjectMeta{ 1625 Name: "ext", 1626 Namespace: "default", 1627 Labels: map[string]string{ 1628 "app.kubernetes.io/part-of": "argocd", 1629 }, 1630 }, 1631 Data: map[string][]byte{ 1632 "issuerSecret": []byte("https://dev-123456.oktapreview.com"), 1633 "clientSecret": []byte("deadbeef"), 1634 "webhook.github.secret": []byte("mywebhooksecret"), 1635 }, 1636 } 1637 kubeClient := fake.NewClientset(cm, secret, argocdSecret) 1638 settingsManager := NewSettingsManager(t.Context(), kubeClient, "default") 1639 1640 settings, err := settingsManager.GetSettings() 1641 require.NoError(t, err) 1642 assert.Equal(t, "mywebhooksecret", settings.GetWebhookGitHubSecret()) 1643 1644 oidcConfig := settings.OIDCConfig() 1645 assert.Equal(t, "https://dev-123456.oktapreview.com", oidcConfig.Issuer) 1646 assert.Equal(t, "deadbeef", oidcConfig.ClientSecret) 1647 } 1648 1649 func TestGetEnableManifestGeneration(t *testing.T) { 1650 testCases := []struct { 1651 name string 1652 enabled bool 1653 data map[string]string 1654 source string 1655 }{{ 1656 name: "default", 1657 enabled: true, 1658 data: map[string]string{}, 1659 source: string(v1alpha1.ApplicationSourceTypeKustomize), 1660 }, { 1661 name: "disabled", 1662 enabled: false, 1663 data: map[string]string{"kustomize.enable": `false`}, 1664 source: string(v1alpha1.ApplicationSourceTypeKustomize), 1665 }, { 1666 name: "enabled", 1667 enabled: true, 1668 data: map[string]string{"kustomize.enable": `true`}, 1669 source: string(v1alpha1.ApplicationSourceTypeKustomize), 1670 }} 1671 for i := range testCases { 1672 tc := testCases[i] 1673 t.Run(tc.name, func(t *testing.T) { 1674 cm := &corev1.ConfigMap{ 1675 ObjectMeta: metav1.ObjectMeta{ 1676 Name: common.ArgoCDConfigMapName, 1677 Namespace: "default", 1678 Labels: map[string]string{ 1679 "app.kubernetes.io/part-of": "argocd", 1680 }, 1681 }, 1682 Data: tc.data, 1683 } 1684 argocdSecret := &corev1.Secret{ 1685 ObjectMeta: metav1.ObjectMeta{ 1686 Name: common.ArgoCDSecretName, 1687 Namespace: "default", 1688 }, 1689 Data: map[string][]byte{ 1690 "admin.password": nil, 1691 "server.secretkey": nil, 1692 }, 1693 } 1694 1695 kubeClient := fake.NewClientset(cm, argocdSecret) 1696 settingsManager := NewSettingsManager(t.Context(), kubeClient, "default") 1697 1698 enableManifestGeneration, err := settingsManager.GetEnabledSourceTypes() 1699 require.NoError(t, err) 1700 1701 assert.Equal(t, enableManifestGeneration[tc.source], tc.enabled) 1702 }) 1703 } 1704 } 1705 1706 func TestGetHelmSettings(t *testing.T) { 1707 testCases := []struct { 1708 name string 1709 data map[string]string 1710 expected []string 1711 }{{ 1712 name: "Default", 1713 data: map[string]string{}, 1714 expected: []string{"http", "https"}, 1715 }, { 1716 name: "Configured Not Empty", 1717 data: map[string]string{ 1718 "helm.valuesFileSchemes": "s3, git", 1719 }, 1720 expected: []string{"s3", "git"}, 1721 }, { 1722 name: "Configured Empty", 1723 data: map[string]string{ 1724 "helm.valuesFileSchemes": "", 1725 }, 1726 expected: nil, 1727 }} 1728 1729 for i := range testCases { 1730 tc := testCases[i] 1731 t.Run(tc.name, func(t *testing.T) { 1732 cm := &corev1.ConfigMap{ 1733 ObjectMeta: metav1.ObjectMeta{ 1734 Name: common.ArgoCDConfigMapName, 1735 Namespace: "default", 1736 Labels: map[string]string{ 1737 "app.kubernetes.io/part-of": "argocd", 1738 }, 1739 }, 1740 Data: tc.data, 1741 } 1742 argocdSecret := &corev1.Secret{ 1743 ObjectMeta: metav1.ObjectMeta{ 1744 Name: common.ArgoCDSecretName, 1745 Namespace: "default", 1746 }, 1747 Data: map[string][]byte{ 1748 "admin.password": nil, 1749 "server.secretkey": nil, 1750 }, 1751 } 1752 secret := &corev1.Secret{ 1753 ObjectMeta: metav1.ObjectMeta{ 1754 Name: "acme", 1755 Namespace: "default", 1756 Labels: map[string]string{ 1757 "app.kubernetes.io/part-of": "argocd", 1758 }, 1759 }, 1760 Data: map[string][]byte{ 1761 "clientSecret": []byte("deadbeef"), 1762 }, 1763 } 1764 kubeClient := fake.NewClientset(cm, secret, argocdSecret) 1765 settingsManager := NewSettingsManager(t.Context(), kubeClient, "default") 1766 1767 helmSettings, err := settingsManager.GetHelmSettings() 1768 require.NoError(t, err) 1769 1770 assert.ElementsMatch(t, tc.expected, helmSettings.ValuesFileSchemes) 1771 }) 1772 } 1773 } 1774 1775 func TestArgoCDSettings_OIDCTLSConfig_OIDCTLSInsecureSkipVerify(t *testing.T) { 1776 certParsed, err := tls.X509KeyPair(test.Cert, test.PrivateKey) 1777 require.NoError(t, err) 1778 1779 testCases := []struct { 1780 name string 1781 settings *ArgoCDSettings 1782 expectNilTLSConfig bool 1783 }{ 1784 { 1785 name: "OIDC configured, no root CA", 1786 settings: &ArgoCDSettings{OIDCConfigRAW: `name: Test 1787 issuer: aaa 1788 clientID: xxx 1789 clientSecret: yyy 1790 requestedScopes: ["oidc"]`}, 1791 }, 1792 { 1793 name: "OIDC configured, valid root CA", 1794 settings: &ArgoCDSettings{OIDCConfigRAW: fmt.Sprintf(` 1795 name: Test 1796 issuer: aaa 1797 clientID: xxx 1798 clientSecret: yyy 1799 requestedScopes: ["oidc"] 1800 rootCA: | 1801 %s 1802 `, strings.ReplaceAll(string(test.Cert), "\n", "\n "))}, 1803 }, 1804 { 1805 name: "OIDC configured, invalid root CA", 1806 settings: &ArgoCDSettings{OIDCConfigRAW: `name: Test 1807 issuer: aaa 1808 clientID: xxx 1809 clientSecret: yyy 1810 requestedScopes: ["oidc"] 1811 rootCA: "invalid"`}, 1812 }, 1813 { 1814 name: "OIDC not configured, no cert configured", 1815 settings: &ArgoCDSettings{}, 1816 expectNilTLSConfig: true, 1817 }, 1818 { 1819 name: "OIDC not configured, cert configured", 1820 settings: &ArgoCDSettings{Certificate: &certParsed}, 1821 }, 1822 } 1823 1824 for _, testCase := range testCases { 1825 testCase := testCase 1826 1827 t.Run(testCase.name, func(t *testing.T) { 1828 if testCase.expectNilTLSConfig { 1829 assert.Nil(t, testCase.settings.OIDCTLSConfig()) 1830 } else { 1831 assert.False(t, testCase.settings.OIDCTLSConfig().InsecureSkipVerify) 1832 1833 testCase.settings.OIDCTLSInsecureSkipVerify = true 1834 1835 assert.True(t, testCase.settings.OIDCTLSConfig().InsecureSkipVerify) 1836 } 1837 }) 1838 } 1839 } 1840 1841 func Test_OAuth2AllowedAudiences(t *testing.T) { 1842 t.Parallel() 1843 1844 testCases := []struct { 1845 name string 1846 settings *ArgoCDSettings 1847 expected []string 1848 }{ 1849 { 1850 name: "Empty", 1851 settings: &ArgoCDSettings{}, 1852 expected: []string{}, 1853 }, 1854 { 1855 name: "OIDC configured, no audiences specified, clientID used", 1856 settings: &ArgoCDSettings{OIDCConfigRAW: `name: Test 1857 issuer: aaa 1858 clientID: xxx 1859 clientSecret: yyy 1860 requestedScopes: ["oidc"]`}, 1861 expected: []string{"xxx"}, 1862 }, 1863 { 1864 name: "OIDC configured, no audiences specified, clientID and cliClientID used", 1865 settings: &ArgoCDSettings{OIDCConfigRAW: `name: Test 1866 issuer: aaa 1867 clientID: xxx 1868 cliClientID: cli-xxx 1869 clientSecret: yyy 1870 requestedScopes: ["oidc"]`}, 1871 expected: []string{"xxx", "cli-xxx"}, 1872 }, 1873 { 1874 name: "OIDC configured, audiences specified", 1875 settings: &ArgoCDSettings{OIDCConfigRAW: `name: Test 1876 issuer: aaa 1877 clientID: xxx 1878 clientSecret: yyy 1879 requestedScopes: ["oidc"] 1880 allowedAudiences: ["aud1", "aud2"]`}, 1881 expected: []string{"aud1", "aud2"}, 1882 }, 1883 { 1884 name: "Dex configured", 1885 settings: &ArgoCDSettings{DexConfig: `connectors: 1886 - type: github 1887 id: github 1888 name: GitHub 1889 config: 1890 clientID: aabbccddeeff00112233 1891 clientSecret: $dex.github.clientSecret 1892 orgs: 1893 - name: your-github-org 1894 `}, 1895 expected: []string{common.ArgoCDClientAppID, common.ArgoCDCLIClientAppID}, 1896 }, 1897 } 1898 1899 for _, tc := range testCases { 1900 tcc := tc 1901 t.Run(tcc.name, func(t *testing.T) { 1902 t.Parallel() 1903 assert.ElementsMatch(t, tcc.expected, tcc.settings.OAuth2AllowedAudiences()) 1904 }) 1905 } 1906 } 1907 1908 func TestReplaceStringSecret(t *testing.T) { 1909 secretValues := map[string]string{"my-secret-key": "my-secret-value"} 1910 result := ReplaceStringSecret("$my-secret-key", secretValues) 1911 assert.Equal(t, "my-secret-value", result) 1912 1913 result = ReplaceStringSecret("$invalid-secret-key", secretValues) 1914 assert.Equal(t, "$invalid-secret-key", result) 1915 1916 result = ReplaceStringSecret("", secretValues) 1917 assert.Empty(t, result) 1918 1919 result = ReplaceStringSecret("my-value", secretValues) 1920 assert.Equal(t, "my-value", result) 1921 } 1922 1923 func TestRedirectURLForRequest(t *testing.T) { 1924 generateRequest := func(url string) *http.Request { 1925 r, err := http.NewRequest(http.MethodPost, url, http.NoBody) 1926 require.NoError(t, err) 1927 return r 1928 } 1929 1930 testCases := []struct { 1931 Name string 1932 Settings *ArgoCDSettings 1933 Request *http.Request 1934 ExpectedURL string 1935 ExpectError bool 1936 }{ 1937 { 1938 Name: "Single URL", 1939 Settings: &ArgoCDSettings{ 1940 URL: "https://example.org", 1941 }, 1942 Request: generateRequest("https://example.org/login"), 1943 ExpectedURL: "https://example.org/auth/callback", 1944 ExpectError: false, 1945 }, 1946 { 1947 Name: "Request does not match configured URL.", 1948 Settings: &ArgoCDSettings{ 1949 URL: "https://otherhost.org", 1950 }, 1951 Request: generateRequest("https://example.org/login"), 1952 ExpectedURL: "https://otherhost.org/auth/callback", 1953 ExpectError: false, 1954 }, 1955 { 1956 Name: "Cannot parse URL.", 1957 Settings: &ArgoCDSettings{ 1958 URL: ":httpsotherhostorg", 1959 }, 1960 Request: generateRequest("https://example.org/login"), 1961 ExpectedURL: "", 1962 ExpectError: true, 1963 }, 1964 { 1965 Name: "Match extended URL in settings.URL.", 1966 Settings: &ArgoCDSettings{ 1967 URL: "https://otherhost.org", 1968 AdditionalURLs: []string{"https://anotherhost.org"}, 1969 }, 1970 Request: generateRequest("https://anotherhost.org/login"), 1971 ExpectedURL: "https://anotherhost.org/auth/callback", 1972 ExpectError: false, 1973 }, 1974 } 1975 1976 for _, tc := range testCases { 1977 t.Run(tc.Name, func(t *testing.T) { 1978 result, err := tc.Settings.RedirectURLForRequest(tc.Request) 1979 assert.Equal(t, tc.ExpectedURL, result) 1980 if tc.ExpectError { 1981 assert.Error(t, err) 1982 } else { 1983 assert.NoError(t, err) 1984 } 1985 }) 1986 } 1987 } 1988 1989 func TestRedirectAdditionalURLs(t *testing.T) { 1990 testCases := []struct { 1991 Name string 1992 Settings *ArgoCDSettings 1993 ExpectedResult []string 1994 ExpectedError bool 1995 }{ 1996 { 1997 Name: "Good case with two AdditionalURLs", 1998 Settings: &ArgoCDSettings{ 1999 URL: "https://example.org", 2000 AdditionalURLs: []string{"https://anotherhost.org", "https://yetanother.org"}, 2001 }, 2002 ExpectedResult: []string{ 2003 "https://anotherhost.org/auth/callback", 2004 "https://yetanother.org/auth/callback", 2005 }, 2006 ExpectedError: false, 2007 }, 2008 { 2009 Name: "Bad URL causes error", 2010 Settings: &ArgoCDSettings{ 2011 URL: "https://example.org", 2012 AdditionalURLs: []string{":httpsotherhostorg"}, 2013 }, 2014 ExpectedResult: []string{}, 2015 ExpectedError: true, 2016 }, 2017 } 2018 2019 for _, tc := range testCases { 2020 t.Run(tc.Name, func(t *testing.T) { 2021 result, err := tc.Settings.RedirectAdditionalURLs() 2022 if tc.ExpectedError { 2023 require.Error(t, err) 2024 } else { 2025 require.NoError(t, err) 2026 } 2027 require.Equal(t, tc.ExpectedResult, result) 2028 }) 2029 } 2030 } 2031 2032 func TestUseAzureWorkloadIdentity(t *testing.T) { 2033 testCases := []struct { 2034 Name string 2035 Settings *ArgoCDSettings 2036 ExpectedResult bool 2037 }{ 2038 { 2039 Name: "UseAzureWorkloadIdentity defined and set to true", 2040 Settings: &ArgoCDSettings{ 2041 OIDCConfigRAW: "{ \"azure\": {\"useWorkloadIdentity\": true }}", 2042 }, 2043 ExpectedResult: true, 2044 }, 2045 { 2046 Name: "UseAzureWorkloadIdentity defined and set to false", 2047 Settings: &ArgoCDSettings{ 2048 OIDCConfigRAW: "{ \"azure\": {\"useWorkloadIdentity\": false }}", 2049 }, 2050 ExpectedResult: false, 2051 }, 2052 { 2053 Name: "UseAzureWorkloadIdentity not defined, with azure key present", 2054 Settings: &ArgoCDSettings{ 2055 OIDCConfigRAW: "{ \"azure\": {}}", 2056 }, 2057 ExpectedResult: false, 2058 }, 2059 { 2060 Name: "UseAzureWorkloadIdentity not defined", 2061 Settings: &ArgoCDSettings{ 2062 OIDCConfigRAW: "{}", 2063 }, 2064 ExpectedResult: false, 2065 }, 2066 { 2067 Name: "OIDC config isnot defined", 2068 Settings: &ArgoCDSettings{}, 2069 ExpectedResult: false, 2070 }, 2071 } 2072 2073 for _, tc := range testCases { 2074 t.Run(tc.Name, func(t *testing.T) { 2075 result := tc.Settings.UseAzureWorkloadIdentity() 2076 require.Equal(t, tc.ExpectedResult, result) 2077 }) 2078 } 2079 } 2080 2081 func TestIsImpersonationEnabled(t *testing.T) { 2082 // When there is no argocd-cm itself, 2083 // Then IsImpersonationEnabled() must return false (default value) and an error with appropriate error message. 2084 kubeClient := fake.NewClientset() 2085 settingsManager := NewSettingsManager(t.Context(), kubeClient, "default") 2086 featureFlag, err := settingsManager.IsImpersonationEnabled() 2087 require.False(t, featureFlag, 2088 "with no argocd-cm config map, IsImpersonationEnabled() must return return false (default value)") 2089 require.ErrorContains(t, err, "configmap \"argocd-cm\" not found", 2090 "with no argocd-cm config map, IsImpersonationEnabled() must return an error") 2091 2092 // When there is no impersonation feature flag present in the argocd-cm, 2093 // Then IsImpersonationEnabled() must return false (default value) and nil error. 2094 _, settingsManager = fixtures(map[string]string{}) 2095 featureFlag, err = settingsManager.IsImpersonationEnabled() 2096 require.False(t, featureFlag, 2097 "with empty argocd-cm config map, IsImpersonationEnabled() must return false (default value)") 2098 require.NoError(t, err, 2099 "with empty argocd-cm config map, IsImpersonationEnabled() must not return any error") 2100 2101 // When user disables the feature explicitly, 2102 // Then IsImpersonationEnabled() must return false and nil error. 2103 _, settingsManager = fixtures(map[string]string{ 2104 "application.sync.impersonation.enabled": "false", 2105 }) 2106 featureFlag, err = settingsManager.IsImpersonationEnabled() 2107 require.False(t, featureFlag, 2108 "when user enables the flag in argocd-cm config map, IsImpersonationEnabled() must return user set value") 2109 require.NoError(t, err, 2110 "when user enables the flag in argocd-cm config map, IsImpersonationEnabled() must not return any error") 2111 2112 // When user enables the feature explicitly, 2113 // Then IsImpersonationEnabled() must return true and nil error. 2114 _, settingsManager = fixtures(map[string]string{ 2115 "application.sync.impersonation.enabled": "true", 2116 }) 2117 featureFlag, err = settingsManager.IsImpersonationEnabled() 2118 require.True(t, featureFlag, 2119 "when user enables the flag in argocd-cm config map, IsImpersonationEnabled() must return user set value") 2120 require.NoError(t, err, 2121 "when user enables the flag in argocd-cm config map, IsImpersonationEnabled() must not return any error") 2122 } 2123 2124 func TestSettingsManager_GetHideSecretAnnotations(t *testing.T) { 2125 tests := []struct { 2126 name string 2127 input string 2128 output map[string]bool 2129 }{ 2130 { 2131 name: "Empty input", 2132 input: "", 2133 output: map[string]bool{}, 2134 }, 2135 { 2136 name: "Comma separated data", 2137 input: "example.com/token-secret.value,token,key", 2138 output: map[string]bool{"example.com/token-secret.value": true, "token": true, "key": true}, 2139 }, 2140 { 2141 name: "Comma separated data with space", 2142 input: "example.com/token-secret.value, token, key", 2143 output: map[string]bool{"example.com/token-secret.value": true, "token": true, "key": true}, 2144 }, 2145 } 2146 for _, tt := range tests { 2147 t.Run(tt.name, func(t *testing.T) { 2148 _, settingsManager := fixtures(map[string]string{ 2149 resourceSensitiveAnnotationsKey: tt.input, 2150 }) 2151 keys := settingsManager.GetSensitiveAnnotations() 2152 assert.Len(t, keys, len(tt.output)) 2153 assert.Equal(t, tt.output, keys) 2154 }) 2155 } 2156 } 2157 2158 func TestSettingsManager_GetAllowedNodeLabels(t *testing.T) { 2159 tests := []struct { 2160 name string 2161 input string 2162 output []string 2163 }{ 2164 { 2165 name: "Empty input", 2166 input: "", 2167 output: []string{}, 2168 }, 2169 { 2170 name: "Comma separated data", 2171 input: "example.com/label,label1,label2", 2172 output: []string{"example.com/label", "label1", "label2"}, 2173 }, 2174 { 2175 name: "Comma separated data with space", 2176 input: "example.com/label, label1, label2", 2177 output: []string{"example.com/label", "label1", "label2"}, 2178 }, 2179 { 2180 name: "Comma separated data with invalid label", 2181 input: "example.com/label,_invalid,label1,label2", 2182 output: []string{"example.com/label", "label1", "label2"}, 2183 }, 2184 } 2185 for _, tt := range tests { 2186 t.Run(tt.name, func(t *testing.T) { 2187 _, settingsManager := fixtures(map[string]string{ 2188 allowedNodeLabelsKey: tt.input, 2189 }) 2190 keys := settingsManager.GetAllowedNodeLabels() 2191 assert.Len(t, keys, len(tt.output)) 2192 assert.Equal(t, tt.output, keys) 2193 }) 2194 } 2195 }