github.com/argoproj-labs/argocd-operator@v0.10.0/controllers/argocd/keycloak_test.go (about) 1 // Copyright 2021 ArgoCD Operator Developers 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package argocd 16 17 import ( 18 "context" 19 "fmt" 20 "testing" 21 22 oappsv1 "github.com/openshift/api/apps/v1" 23 routev1 "github.com/openshift/api/route/v1" 24 templatev1 "github.com/openshift/api/template/v1" 25 "github.com/stretchr/testify/assert" 26 corev1 "k8s.io/api/core/v1" 27 resourcev1 "k8s.io/apimachinery/pkg/api/resource" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/runtime" 30 "sigs.k8s.io/controller-runtime/pkg/client" 31 32 argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1" 33 "github.com/argoproj-labs/argocd-operator/common" 34 "github.com/argoproj-labs/argocd-operator/controllers/argoutil" 35 ) 36 37 var ( 38 fakeNs = "foo" 39 fakeReplicas int32 = 1 40 fakeVolumes = []corev1.Volume{ 41 { 42 Name: "sso-x509-https-volume", 43 VolumeSource: corev1.VolumeSource{ 44 Secret: &corev1.SecretVolumeSource{ 45 SecretName: servingCertSecretName, 46 }, 47 }, 48 }, 49 { 50 Name: "service-ca", 51 VolumeSource: corev1.VolumeSource{ 52 ConfigMap: &corev1.ConfigMapVolumeSource{ 53 LocalObjectReference: corev1.LocalObjectReference{ 54 Name: "${APPLICATION_NAME}-service-ca", 55 }, 56 }, 57 }, 58 }, 59 { 60 Name: "sso-probe-netrc-volume", 61 VolumeSource: corev1.VolumeSource{ 62 EmptyDir: &corev1.EmptyDirVolumeSource{ 63 Medium: "Memory", 64 }, 65 }, 66 }, 67 } 68 ) 69 70 func getFakeKeycloakResources() corev1.ResourceRequirements { 71 return corev1.ResourceRequirements{ 72 Requests: corev1.ResourceList{ 73 corev1.ResourceMemory: resourcev1.MustParse("1Mi"), 74 corev1.ResourceCPU: resourcev1.MustParse("1m"), 75 }, 76 Limits: corev1.ResourceList{ 77 corev1.ResourceMemory: resourcev1.MustParse("128Mi"), 78 corev1.ResourceCPU: resourcev1.MustParse("128m"), 79 }, 80 } 81 } 82 83 func TestKeycloakContainerImage(t *testing.T) { 84 85 defer removeTemplateAPI() 86 tests := []struct { 87 name string 88 setEnvVarFunc func(*testing.T, string) 89 envVar string 90 argoCD *argoproj.ArgoCD 91 updateCrFunc func(cr *argoproj.ArgoCD) 92 templateAPIFound bool 93 wantContainerImage string 94 }{ 95 { 96 name: "no .spec.sso, no ArgoCDKeycloakImageEnvName env var set", 97 setEnvVarFunc: nil, 98 envVar: "", 99 argoCD: makeArgoCD(func(cr *argoproj.ArgoCD) { 100 cr.Spec.SSO = &argoproj.ArgoCDSSOSpec{ 101 Provider: argoproj.SSOProviderTypeKeycloak, 102 } 103 }), 104 updateCrFunc: nil, 105 templateAPIFound: false, 106 wantContainerImage: "quay.io/keycloak/keycloak@sha256:64fb81886fde61dee55091e6033481fa5ccdac62ae30a4fd29b54eb5e97df6a9", 107 }, 108 { 109 name: "no .spec.sso, no ArgoCDKeycloakImageEnvName env var set - for OCP", 110 setEnvVarFunc: nil, 111 envVar: "", 112 argoCD: makeArgoCD(func(cr *argoproj.ArgoCD) { 113 cr.Spec.SSO = &argoproj.ArgoCDSSOSpec{ 114 Provider: argoproj.SSOProviderTypeKeycloak, 115 } 116 }), 117 updateCrFunc: nil, 118 templateAPIFound: true, 119 wantContainerImage: "registry.redhat.io/rh-sso-7/sso76-openshift-rhel8@sha256:ec9f60018694dcc5d431ba47d5536b761b71cb3f66684978fe6bb74c157679ac", 120 }, 121 { 122 name: "ArgoCDKeycloakImageEnvName env var set", 123 setEnvVarFunc: func(t *testing.T, s string) { 124 t.Setenv(common.ArgoCDKeycloakImageEnvName, s) 125 }, 126 envVar: "envImage:latest", 127 argoCD: makeArgoCD(func(cr *argoproj.ArgoCD) { 128 cr.Spec.SSO = &argoproj.ArgoCDSSOSpec{ 129 Provider: argoproj.SSOProviderTypeKeycloak, 130 } 131 }), 132 updateCrFunc: nil, 133 templateAPIFound: true, 134 wantContainerImage: "envImage:latest", 135 }, 136 { 137 name: "both cr.spec.sso.keycloak.Image and ArgoCDKeycloakImageEnvName are set", 138 setEnvVarFunc: func(t *testing.T, s string) { 139 t.Setenv(common.ArgoCDKeycloakImageEnvName, s) 140 }, 141 envVar: "envImage:latest", 142 argoCD: makeArgoCD(func(cr *argoproj.ArgoCD) { 143 cr.Spec.SSO = &argoproj.ArgoCDSSOSpec{ 144 Provider: argoproj.SSOProviderTypeKeycloak, 145 } 146 }), 147 updateCrFunc: func(cr *argoproj.ArgoCD) { 148 cr.Spec.SSO = &argoproj.ArgoCDSSOSpec{ 149 Provider: argoproj.SSOProviderTypeKeycloak, 150 Keycloak: &argoproj.ArgoCDKeycloakSpec{ 151 Image: "crImage", 152 Version: "crVersion", 153 }, 154 } 155 }, 156 templateAPIFound: true, 157 wantContainerImage: "crImage:crVersion", 158 }, 159 } 160 161 for _, test := range tests { 162 t.Run(test.name, func(t *testing.T) { 163 templateAPIFound = test.templateAPIFound 164 165 if test.setEnvVarFunc != nil { 166 test.setEnvVarFunc(t, test.envVar) 167 } 168 if test.updateCrFunc != nil { 169 test.updateCrFunc(test.argoCD) 170 } 171 172 testImage := getKeycloakContainerImage(test.argoCD) 173 assert.Equal(t, test.wantContainerImage, testImage) 174 175 }) 176 } 177 } 178 179 func TestNewKeycloakTemplateInstance(t *testing.T) { 180 // For OpenShift Container Platform. 181 templateAPIFound = true 182 defer removeTemplateAPI() 183 184 a := makeTestArgoCD() 185 a.Spec.SSO = &argoproj.ArgoCDSSOSpec{ 186 Provider: "keycloak", 187 } 188 tmplInstance, err := newKeycloakTemplateInstance(a) 189 assert.NoError(t, err) 190 191 assert.Equal(t, tmplInstance.Name, "rhsso") 192 assert.Equal(t, tmplInstance.Namespace, a.Namespace) 193 } 194 195 func TestNewKeycloakTemplate(t *testing.T) { 196 // For OpenShift Container Platform. 197 templateAPIFound = true 198 defer removeTemplateAPI() 199 200 a := makeTestArgoCD() 201 a.Spec.SSO = &argoproj.ArgoCDSSOSpec{ 202 Provider: "keycloak", 203 } 204 tmpl, err := newKeycloakTemplate(a) 205 assert.NoError(t, err) 206 207 assert.Equal(t, tmpl.Name, "rhsso") 208 assert.Equal(t, tmpl.Namespace, a.Namespace) 209 } 210 211 func TestNewKeycloakTemplate_testDeploymentConfig(t *testing.T) { 212 // For OpenShift Container Platform. 213 templateAPIFound = true 214 defer removeTemplateAPI() 215 216 a := makeTestArgoCD() 217 a.Spec.SSO = &argoproj.ArgoCDSSOSpec{ 218 Provider: "keycloak", 219 } 220 dc := getKeycloakDeploymentConfigTemplate(a) 221 222 assert.Equal(t, dc.Spec.Replicas, fakeReplicas) 223 224 strategy := oappsv1.DeploymentStrategy{ 225 Type: "Recreate", 226 Resources: corev1.ResourceRequirements{ 227 Requests: corev1.ResourceList{ 228 corev1.ResourceMemory: resourcev1.MustParse("256Mi"), 229 corev1.ResourceCPU: resourcev1.MustParse("250m"), 230 }, 231 Limits: corev1.ResourceList{ 232 corev1.ResourceMemory: resourcev1.MustParse("512Mi"), 233 corev1.ResourceCPU: resourcev1.MustParse("500m"), 234 }, 235 }, 236 } 237 assert.Equal(t, dc.Spec.Strategy, strategy) 238 239 assert.Equal(t, dc.Spec.Template.ObjectMeta.Name, "${APPLICATION_NAME}") 240 assert.Equal(t, dc.Spec.Template.Spec.Volumes, fakeVolumes) 241 } 242 243 func TestNewKeycloakTemplate_testKeycloakContainer(t *testing.T) { 244 // For OpenShift Container Platform. 245 t.Setenv(common.ArgoCDKeycloakImageEnvName, "") 246 templateAPIFound = true 247 defer removeTemplateAPI() 248 249 a := makeTestArgoCD() 250 a.Spec.SSO = &argoproj.ArgoCDSSOSpec{ 251 Provider: "keycloak", 252 } 253 kc := getKeycloakContainer(a) 254 assert.Equal(t, 255 "registry.redhat.io/rh-sso-7/sso76-openshift-rhel8@sha256:ec9f60018694dcc5d431ba47d5536b761b71cb3f66684978fe6bb74c157679ac", kc.Image) 256 assert.Equal(t, corev1.PullAlways, kc.ImagePullPolicy) 257 assert.Equal(t, "${APPLICATION_NAME}", kc.Name) 258 } 259 260 func TestKeycloakResources(t *testing.T) { 261 defer removeTemplateAPI() 262 fR := getFakeKeycloakResources() 263 264 tests := []struct { 265 name string 266 argoCD *argoproj.ArgoCD 267 updateCrFunc func(cr *argoproj.ArgoCD) 268 wantResources corev1.ResourceRequirements 269 }{ 270 { 271 name: "default", 272 argoCD: makeTestArgoCD(func(cr *argoproj.ArgoCD) { 273 cr.Spec.SSO = &argoproj.ArgoCDSSOSpec{ 274 Provider: argoproj.SSOProviderTypeKeycloak, 275 } 276 }), 277 updateCrFunc: nil, 278 wantResources: defaultKeycloakResources(), 279 }, 280 { 281 name: "override with .spec.sso.keycloak", 282 argoCD: makeTestArgoCD(func(cr *argoproj.ArgoCD) { 283 cr.Spec.SSO = &argoproj.ArgoCDSSOSpec{ 284 Provider: argoproj.SSOProviderTypeKeycloak, 285 } 286 }), 287 updateCrFunc: func(cr *argoproj.ArgoCD) { 288 cr.Spec.SSO = &argoproj.ArgoCDSSOSpec{ 289 Keycloak: &argoproj.ArgoCDKeycloakSpec{ 290 Resources: &fR, 291 }, 292 } 293 }, 294 wantResources: getFakeKeycloakResources(), 295 }, 296 } 297 298 for _, test := range tests { 299 t.Run(test.name, func(t *testing.T) { 300 if test.updateCrFunc != nil { 301 test.updateCrFunc(test.argoCD) 302 } 303 304 testResources := getKeycloakContainer(test.argoCD).Resources 305 assert.Equal(t, test.wantResources, testResources) 306 307 }) 308 } 309 } 310 311 func TestNewKeycloakTemplate_testConfigmap(t *testing.T) { 312 cm := getKeycloakConfigMapTemplate(fakeNs) 313 assert.Equal(t, cm.Name, "${APPLICATION_NAME}-service-ca") 314 assert.Equal(t, cm.Namespace, fakeNs) 315 } 316 317 func TestNewKeycloakTemplate_testService(t *testing.T) { 318 svc := getKeycloakServiceTemplate(fakeNs) 319 assert.Equal(t, svc.Name, "${APPLICATION_NAME}") 320 assert.Equal(t, svc.Namespace, fakeNs) 321 assert.Equal(t, svc.Spec.Selector, map[string]string{ 322 "deploymentConfig": "${APPLICATION_NAME}"}) 323 } 324 325 func TestNewKeycloakTemplate_testRoute(t *testing.T) { 326 route := getKeycloakRouteTemplate(fakeNs) 327 assert.Equal(t, route.Name, "${APPLICATION_NAME}") 328 assert.Equal(t, route.Namespace, fakeNs) 329 assert.Equal(t, route.Spec.To, 330 routev1.RouteTargetReference{Name: "${APPLICATION_NAME}"}) 331 assert.Equal(t, route.Spec.TLS, 332 &routev1.TLSConfig{Termination: "reencrypt"}) 333 } 334 335 func TestKeycloak_testRealmConfigCreation(t *testing.T) { 336 cfg := &keycloakConfig{ 337 ArgoName: "foo-argocd", 338 ArgoNamespace: "foo", 339 Username: "test-user", 340 Password: "test", 341 KeycloakURL: "https://foo.keycloak.com", 342 ArgoCDURL: "https://bar.argocd.com", 343 } 344 345 _, err := createRealmConfig(cfg) 346 assert.NoError(t, err) 347 } 348 349 func TestKeycloak_testServerCert(t *testing.T) { 350 351 a := makeTestArgoCDForKeycloak() 352 353 resObjs := []client.Object{a} 354 subresObjs := []client.Object{a} 355 runtimeObjs := []runtime.Object{} 356 sch := makeTestReconcilerScheme(argoproj.AddToScheme, templatev1.Install, oappsv1.Install, routev1.Install) 357 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 358 r := makeTestReconciler(cl, sch) 359 360 sslCertsSecret := &corev1.Secret{ 361 ObjectMeta: metav1.ObjectMeta{ 362 Name: servingCertSecretName, 363 Namespace: a.Namespace, 364 }, 365 Data: map[string][]byte{ 366 "tls.crt": []byte("asdasfsff"), 367 }, 368 } 369 r.Client.Create(context.TODO(), sslCertsSecret) 370 371 _, err := r.getKCServerCert(a) 372 assert.NoError(t, err) 373 374 sslCertsSecret.Data["tls.crt"] = nil 375 assert.NoError(t, r.Client.Update(context.TODO(), sslCertsSecret)) 376 377 _, err = r.getKCServerCert(a) 378 assert.NoError(t, err) 379 } 380 381 func TestKeycloakConfigVerifyTLSForOpenShift(t *testing.T) { 382 tests := []struct { 383 name string 384 argoCD *argoproj.ArgoCD 385 desiredVerifyTLS bool 386 }{ 387 { 388 name: ".spec.sso.keycloak.verifyTLS nil", 389 argoCD: makeTestArgoCD(func(ac *argoproj.ArgoCD) { 390 ac.Spec.SSO = &argoproj.ArgoCDSSOSpec{ 391 Provider: argoproj.SSOProviderTypeKeycloak, 392 } 393 }), 394 desiredVerifyTLS: true, 395 }, 396 { 397 name: ".spec.sso.keycloak.verifyTLS false", 398 argoCD: makeTestArgoCD(func(ac *argoproj.ArgoCD) { 399 ac.Spec.SSO = &argoproj.ArgoCDSSOSpec{ 400 Provider: argoproj.SSOProviderTypeKeycloak, 401 Keycloak: &argoproj.ArgoCDKeycloakSpec{ 402 VerifyTLS: boolPtr(false), 403 }, 404 } 405 }), 406 desiredVerifyTLS: false, 407 }, 408 { 409 name: ".spec.sso.keycloak.verifyTLS true", 410 argoCD: makeTestArgoCD(func(ac *argoproj.ArgoCD) { 411 ac.Spec.SSO = &argoproj.ArgoCDSSOSpec{ 412 Provider: argoproj.SSOProviderTypeKeycloak, 413 Keycloak: &argoproj.ArgoCDKeycloakSpec{ 414 VerifyTLS: boolPtr(true), 415 }, 416 } 417 }), 418 desiredVerifyTLS: true, 419 }, 420 } 421 422 for _, test := range tests { 423 t.Run(test.name, func(t *testing.T) { 424 425 resObjs := []client.Object{test.argoCD} 426 subresObjs := []client.Object{test.argoCD} 427 runtimeObjs := []runtime.Object{} 428 sch := makeTestReconcilerScheme(argoproj.AddToScheme, templatev1.Install, oappsv1.Install, routev1.Install) 429 cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) 430 r := makeTestReconciler(cl, sch) 431 432 keycloakRoute := &routev1.Route{ 433 ObjectMeta: metav1.ObjectMeta{ 434 Name: defaultKeycloakIdentifier, 435 Namespace: test.argoCD.Namespace, 436 }, 437 Spec: routev1.RouteSpec{ 438 Host: "test-host", 439 }, 440 } 441 r.Client.Create(context.TODO(), keycloakRoute) 442 443 argoCDRoute := &routev1.Route{ 444 ObjectMeta: metav1.ObjectMeta{ 445 Name: fmt.Sprintf("%s-%s", test.argoCD.Name, "server"), 446 Namespace: test.argoCD.Namespace, 447 }, 448 Spec: routev1.RouteSpec{ 449 Host: "test-argocd-host", 450 }, 451 } 452 r.Client.Create(context.TODO(), argoCDRoute) 453 454 keycloakSecret := &corev1.Secret{ 455 ObjectMeta: metav1.ObjectMeta{ 456 Name: fmt.Sprintf("%s-%s", defaultKeycloakIdentifier, "secret"), 457 Namespace: test.argoCD.Namespace, 458 }, 459 Data: map[string][]byte{"SSO_USERNAME": []byte("username"), "SSO_PASSWORD": []byte("password")}, 460 } 461 r.Client.Create(context.TODO(), keycloakSecret) 462 463 sslCertsSecret := &corev1.Secret{ 464 ObjectMeta: metav1.ObjectMeta{ 465 Name: servingCertSecretName, 466 Namespace: test.argoCD.Namespace, 467 }, 468 Data: map[string][]byte{ 469 "tls.crt": []byte("asdasfsff"), 470 }, 471 } 472 r.Client.Create(context.TODO(), sslCertsSecret) 473 474 keyCloakConfig, err := r.prepareKeycloakConfig(test.argoCD) 475 assert.NoError(t, err) 476 477 assert.Equal(t, test.desiredVerifyTLS, keyCloakConfig.VerifyTLS) 478 }) 479 } 480 } 481 482 func TestKeycloak_NodeLabelSelector(t *testing.T) { 483 a := makeTestArgoCDForKeycloak() 484 a.Spec.NodePlacement = &argoproj.ArgoCDNodePlacementSpec{ 485 NodeSelector: deploymentDefaultNodeSelector(), 486 Tolerations: deploymentDefaultTolerations(), 487 } 488 489 dc := getKeycloakDeploymentConfigTemplate(a) 490 491 nSelectors := deploymentDefaultNodeSelector() 492 nSelectors = argoutil.AppendStringMap(nSelectors, common.DefaultNodeSelector()) 493 assert.Equal(t, dc.Spec.Template.Spec.NodeSelector, nSelectors) 494 assert.Equal(t, dc.Spec.Template.Spec.Tolerations, a.Spec.NodePlacement.Tolerations) 495 } 496 497 func removeTemplateAPI() { 498 templateAPIFound = false 499 }