diff --git a/operator/src/main/java/org/keycloak/operator/Utils.java b/operator/src/main/java/org/keycloak/operator/Utils.java new file mode 100644 index 0000000000..bd29c1d082 --- /dev/null +++ b/operator/src/main/java/org/keycloak/operator/Utils.java @@ -0,0 +1,29 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.operator; + +import io.fabric8.kubernetes.client.KubernetesClient; + +/** + * @author Vaclav Muzikar + */ +public final class Utils { + public static boolean isOpenShift(KubernetesClient client) { + return client.supports("operator.openshift.io/v1", "OpenShiftAPIServer"); + } +} diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java index be3fc7bf85..ca101362d1 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java @@ -51,6 +51,7 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; +import static org.keycloak.operator.Utils.isOpenShift; import static org.keycloak.operator.testsuite.utils.K8sUtils.getResourceFromFile; public abstract class BaseOperatorTest { @@ -75,6 +76,7 @@ public abstract class BaseOperatorTest { protected static String kubernetesIp; protected static String customImage; private static Operator operator; + protected static boolean isOpenShift; @BeforeAll @@ -91,6 +93,7 @@ public abstract class BaseOperatorTest { createK8sClient(); createCRDs(); createNamespace(); + isOpenShift = isOpenShift(k8sclient); if (operatorDeployment == OperatorDeployment.remote) { createRBACresourcesAndOperatorDeployment(); diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/integration/KeycloakIngressTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/integration/KeycloakIngressTest.java index df6d186638..f900804f20 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/integration/KeycloakIngressTest.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/integration/KeycloakIngressTest.java @@ -25,6 +25,7 @@ import io.quarkus.logging.Log; import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; import org.awaitility.Awaitility; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.keycloak.operator.Constants; import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak; @@ -41,59 +42,80 @@ import static org.junit.jupiter.api.Assertions.assertEquals; @QuarkusTest public class KeycloakIngressTest extends BaseOperatorTest { + private static String baseDomain; + + @BeforeAll + public static void beforeKeycloakIngressTest() { + if (isOpenShift) { + Log.info("OpenShift detected, using real domain"); + // see https://docs.openshift.com/container-platform/4.12/networking/ingress-operator.html#configuring-ingress + baseDomain = k8sclient.genericKubernetesResources("config.openshift.io/v1", "Ingress") + .withName("cluster") + .get() + .get("spec", "domain"); + if (baseDomain == null || baseDomain.isBlank()) { + throw new IllegalStateException("Couldn't fetch the base Ingress domain"); + } + } + } @Test public void testIngressOnHTTP() { var kc = K8sUtils.getDefaultKeycloakDeployment(); kc.getSpec().getHttpSpec().setTlsSecret(null); kc.getSpec().getHttpSpec().setHttpEnabled(true); - var hostnameSpec = new HostnameSpecBuilder() + var hostnameSpecBuilder = new HostnameSpecBuilder() .withStrict(false) - .withStrictBackchannel(false) - .build(); - kc.getSpec().setHostnameSpec(hostnameSpec); + .withStrictBackchannel(false); + String testHostname; + String baseUrl; + if (isOpenShift) { + testHostname = "kc-http-" + namespace + "." + baseDomain; + // on OpenShift, when Keycloak is configured for HTTP only, we use edge TLS termination, i.e. Route still uses TLS + baseUrl = "https://" + testHostname + ":443"; + hostnameSpecBuilder.withHostname(testHostname); + } + else { + baseUrl = "http://" + kubernetesIp + ":80"; + } + kc.getSpec().setHostnameSpec(hostnameSpecBuilder.build()); + K8sUtils.deployKeycloak(k8sclient, kc, true); - Awaitility.await() - .ignoreExceptions() - .untilAsserted(() -> { - var output = RestAssured.given() - .get("http://" + kubernetesIp + ":80/realms/master") - .body() - .jsonPath() - .getString("realm"); - - assertEquals("master", output); - }); - - Awaitility.await() - .ignoreExceptions() - .untilAsserted(() -> { - var statusCode = RestAssured.given() - .get("http://" + kubernetesIp + ":80/admin/master/console") - .statusCode(); - - assertEquals(200, statusCode); - }); + testIngressURLs(baseUrl); } @Test public void testIngressOnHTTPS() { var kc = K8sUtils.getDefaultKeycloakDeployment(); - var hostnameSpec = new HostnameSpecBuilder() + var hostnameSpecBuilder = new HostnameSpecBuilder() .withStrict(false) - .withStrictBackchannel(false) - .build(); - kc.getSpec().setHostnameSpec(hostnameSpec); + .withStrictBackchannel(false); + String testHostname; + if (isOpenShift) { + testHostname = "kc-https-" + namespace + "." + baseDomain; + hostnameSpecBuilder.withHostname(testHostname); + } + else { + testHostname = kubernetesIp; + } + kc.getSpec().setHostnameSpec(hostnameSpecBuilder.build()); K8sUtils.deployKeycloak(k8sclient, kc, true); + testIngressURLs("https://" + testHostname + ":443"); + } + + private void testIngressURLs(String baseUrl) { Awaitility.await() .ignoreExceptions() .untilAsserted(() -> { + var url = baseUrl + "/realms/master"; + Log.info("Testing URL: " + url); + var output = RestAssured.given() .relaxedHTTPSValidation() - .get("https://" + kubernetesIp + ":443/realms/master") + .get(url) .body() .jsonPath() .getString("realm"); @@ -104,9 +126,12 @@ public class KeycloakIngressTest extends BaseOperatorTest { Awaitility.await() .ignoreExceptions() .untilAsserted(() -> { + var url = baseUrl + "/admin/master/console"; + Log.info("Testing URL: " + url); + var statusCode = RestAssured.given() .relaxedHTTPSValidation() - .get("https://" + kubernetesIp + ":443/admin/master/console") + .get(url) .statusCode(); assertEquals(200, statusCode);