This is an automated email from the ASF dual-hosted git repository. jamesnetherton pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
The following commit(s) were added to refs/heads/main by this push: new aec25ff1d4 Improve container setup for kafka-oauth test aec25ff1d4 is described below commit aec25ff1d4aa8f6b211fcef9452d0f6126e5a7cf Author: James Netherton <jamesnether...@gmail.com> AuthorDate: Thu Nov 16 12:11:12 2023 +0000 Improve container setup for kafka-oauth test --- integration-tests/kafka-oauth/pom.xml | 11 +-- .../src/main/resources/application.properties | 11 +-- .../kafka/oauth/it/KafkaKeycloakTestResource.java | 80 ++++++++++++++++++++-- .../oauth/it/container/KeycloakContainer.java | 61 ----------------- .../src/test/resources/kafkaServer.properties | 15 +--- pom.xml | 1 + 6 files changed, 84 insertions(+), 95 deletions(-) diff --git a/integration-tests/kafka-oauth/pom.xml b/integration-tests/kafka-oauth/pom.xml index 7542b7eca6..8e01b47e85 100644 --- a/integration-tests/kafka-oauth/pom.xml +++ b/integration-tests/kafka-oauth/pom.xml @@ -88,6 +88,11 @@ <artifactId>strimzi-test-container</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-test-keycloak-server</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers</artifactId> @@ -126,8 +131,7 @@ <artifactId>maven-surefire-plugin</artifactId> <configuration> <systemPropertyVariables> - <!-- Configure the app to test to resolve "keycloak" hostname to the Docker hostname --> - <jdk.net.hosts.file>target/hosts</jdk.net.hosts.file> + <keycloak.docker.image>${keycloak.container.image}</keycloak.docker.image> </systemPropertyVariables> </configuration> </plugin> @@ -156,9 +160,8 @@ <goal>verify</goal> </goals> <configuration> - <rerunFailingTestsCount>${rerun.failing.test.count}</rerunFailingTestsCount> <systemPropertyVariables> - <quarkus.test.arg-line>-Djdk.net.hosts.file=target/hosts</quarkus.test.arg-line> + <keycloak.docker.image>${keycloak.container.image}</keycloak.docker.image> </systemPropertyVariables> </configuration> </execution> diff --git a/integration-tests/kafka-oauth/src/main/resources/application.properties b/integration-tests/kafka-oauth/src/main/resources/application.properties index 315c07153a..2dd473ffde 100644 --- a/integration-tests/kafka-oauth/src/main/resources/application.properties +++ b/integration-tests/kafka-oauth/src/main/resources/application.properties @@ -23,26 +23,17 @@ kafka.topic.name=test timer.period = 100 timer.delay = 100 -camel.component.kafka.security-protocol = SASL_PLAINTEXT -camel.component.kafka.sasl-mechanism = OAUTHBEARER -camel.component.kafka.sasl-jaas-config = org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required \ - oauth.client.id="kafka-client" \ - oauth.client.secret="kafka-client-secret" \ - oauth.token.endpoint.uri="http://keycloak:8080/auth/realms/kafka-authz/protocol/openid-connect/token" ; -camel.component.kafka.additional-properties[sasl.login.callback.handler.class] = io.strimzi.kafka.oauth.client.JaasClientOauthLoginCallbackHandler - # enable health check quarkus.kafka.health.enabled=true # using QuarkusTestResourceLifecycleManager in this test: Kafka configuration needs to be tuned to work with Keycloak quarkus.kafka.devservices.enabled=false - # # Quarkus - Log # quarkus.log.category."org.apache.camel.quarkus.core.deployment".level = INFO -quarkus.log.category."org.apache.camel.quarkus.component.kafka".level = DEBUG +quarkus.log.category."org.apache.camel.quarkus.component.kafka".level = INFO quarkus.log.category."org.apache.zookeeper".level = WARNING quarkus.log.category."org.apache.kafka".level = WARNING diff --git a/integration-tests/kafka-oauth/src/test/java/org/apache/camel/quarkus/kafka/oauth/it/KafkaKeycloakTestResource.java b/integration-tests/kafka-oauth/src/test/java/org/apache/camel/quarkus/kafka/oauth/it/KafkaKeycloakTestResource.java index 6891a565f3..5554d5202e 100644 --- a/integration-tests/kafka-oauth/src/test/java/org/apache/camel/quarkus/kafka/oauth/it/KafkaKeycloakTestResource.java +++ b/integration-tests/kafka-oauth/src/test/java/org/apache/camel/quarkus/kafka/oauth/it/KafkaKeycloakTestResource.java @@ -16,13 +16,19 @@ */ package org.apache.camel.quarkus.kafka.oauth.it; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; import java.time.Duration; import java.util.HashMap; import java.util.Map; import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; +import io.quarkus.test.keycloak.client.KeycloakTestClient; +import io.quarkus.test.keycloak.server.KeycloakContainer; import io.strimzi.test.container.StrimziKafkaContainer; -import org.apache.camel.quarkus.kafka.oauth.it.container.KeycloakContainer; +import org.apache.commons.io.IOUtils; import org.eclipse.microprofile.config.ConfigProvider; import org.jboss.logging.Logger; import org.testcontainers.utility.MountableFile; @@ -34,7 +40,8 @@ import static io.strimzi.test.container.StrimziKafkaContainer.KAFKA_PORT; */ public class KafkaKeycloakTestResource implements QuarkusTestResourceLifecycleManager { - private static final Logger log = Logger.getLogger(KafkaKeycloakTestResource.class); + private static final Logger LOG = Logger.getLogger(KafkaKeycloakTestResource.class); + private static final String REALM_JSON = "keycloak/realms/kafka-authz-realm.json"; private StrimziKafkaContainer kafka; private KeycloakContainer keycloak; @@ -46,23 +53,56 @@ public class KafkaKeycloakTestResource implements QuarkusTestResourceLifecycleMa keycloak = new KeycloakContainer(); keycloak.withStartupTimeout(Duration.ofMinutes(5)); keycloak.start(); - log.info(keycloak.getLogs()); - keycloak.createHostsFile(); + LOG.info(keycloak.getLogs()); + + Path realmJson = null; + try { + URL resource = Thread.currentThread().getContextClassLoader().getResource(REALM_JSON); + if (resource == null) { + throw new RuntimeException("Unable to load " + REALM_JSON); + } + + realmJson = Files.createTempFile("keycloak-auth", ".json"); + IOUtils.copy(resource, realmJson.toFile()); + + KeycloakTestClient client = new KeycloakTestClient(keycloak.getServerUrl()); + client.createRealmFromPath(realmJson.toAbsolutePath().toString()); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + if (realmJson != null) { + try { + Files.deleteIfExists(realmJson); + } catch (IOException e) { + // Ignored + } + } + } //Start kafka container String imageName = ConfigProvider.getConfig().getValue("strimzi-kafka.container.image", String.class); this.kafka = new StrimziKafkaContainer(imageName) .withBrokerId(1) - .withKafkaConfigurationMap(Map.of("listener.security.protocol.map", "JWT:SASL_PLAINTEXT,BROKER1:PLAINTEXT")) + .withKafkaConfigurationMap(Map.of("listener.security.protocol.map", + "JWT:SASL_PLAINTEXT,BROKER1:PLAINTEXT", + "listener.name.jwt.oauthbearer.sasl.jaas.config", + getOauthSaslJaasConfig(keycloak.getInternalUrl(), keycloak.getServerUrl()), + "listener.name.jwt.plain.sasl.jaas.config", + getPlainSaslJaasConfig(keycloak.getInternalUrl(), keycloak.getServerUrl()))) .withNetworkAliases("kafka") .withServerProperties(MountableFile.forClasspathResource("kafkaServer.properties")) .withBootstrapServers( c -> String.format("JWT://%s:%s", c.getHost(), c.getMappedPort(KAFKA_PORT))); this.kafka.start(); - log.info(this.kafka.getLogs()); + LOG.info(this.kafka.getLogs()); + properties.put("kafka.bootstrap.servers", this.kafka.getBootstrapServers()); properties.put("camel.component.kafka.brokers", kafka.getBootstrapServers()); - + properties.put("camel.component.kafka.security-protocol", "SASL_PLAINTEXT"); + properties.put("camel.component.kafka.sasl-mechanism", "OAUTHBEARER"); + properties.put("camel.component.kafka.additional-properties[sasl.login.callback.handler.class]", + "io.strimzi.kafka.oauth.client.JaasClientOauthLoginCallbackHandler"); + properties.put("camel.component.kafka.sasl-jaas-config", getClientSaslJaasConfig(keycloak.getServerUrl())); return properties; } @@ -75,4 +115,30 @@ public class KafkaKeycloakTestResource implements QuarkusTestResourceLifecycleMa keycloak.stop(); } } + + private String getClientSaslJaasConfig(String keycloakServerUrl) { + return "org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required" + + " oauth.client.id=\"kafka-client\"" + + " oauth.client.secret=\"kafka-client-secret\"" + + " oauth.token.endpoint.uri=\"" + keycloakServerUrl + "/realms/kafka-authz/protocol/openid-connect/token\";"; + } + + private String getPlainSaslJaasConfig(String keycloakInternalUrl, String keycloakServerUrl) { + return "'org.apache.kafka.common.security.plain.PlainLoginModule required " + + "oauth.jwks.endpoint.uri=\"" + keycloakInternalUrl + "/realms/kafka-authz/protocol/openid-connect/certs\" " + + "oauth.valid.issuer.uri=\"" + keycloakServerUrl + "/realms/kafka-authz\" " + + "oauth.token.endpoint.uri=\"" + keycloakInternalUrl + "/realms/kafka-authz/protocol/openid-connect/token\" " + + "oauth.client.id=\"kafka\" " + + "oauth.client.secret=\"kafka-secret\" " + + "unsecuredLoginStringClaim_sub=\"admin\";'"; + } + + private String getOauthSaslJaasConfig(String keycloakInternalUrl, String keycloakServerUrl) { + return "'org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required " + + "oauth.jwks.endpoint.uri=\"" + keycloakInternalUrl + "/realms/kafka-authz/protocol/openid-connect/certs\" " + + "oauth.valid.issuer.uri=\"" + keycloakServerUrl + "/realms/kafka-authz\" " + + "oauth.token.endpoint.uri=\"" + keycloakInternalUrl + "/realms/kafka-authz/protocol/openid-connect/token\" " + + "oauth.client.id=\"kafka\" " + + "oauth.client.secret=\"kafka-secret\";'"; + } } diff --git a/integration-tests/kafka-oauth/src/test/java/org/apache/camel/quarkus/kafka/oauth/it/container/KeycloakContainer.java b/integration-tests/kafka-oauth/src/test/java/org/apache/camel/quarkus/kafka/oauth/it/container/KeycloakContainer.java deleted file mode 100644 index dcf86ae383..0000000000 --- a/integration-tests/kafka-oauth/src/test/java/org/apache/camel/quarkus/kafka/oauth/it/container/KeycloakContainer.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.apache.camel.quarkus.kafka.oauth.it.container; - -import java.io.FileWriter; - -import org.testcontainers.containers.FixedHostPortGenericContainer; -import org.testcontainers.containers.Network; -import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.utility.MountableFile; - -/** - * Inspired from https://github.com/quarkusio/quarkus/tree/main/integration-tests/kafka-oauth-keycloak/ - */ -public class KeycloakContainer extends FixedHostPortGenericContainer<KeycloakContainer> { - - public KeycloakContainer() { - super("quay.io/keycloak/keycloak:16.1.1"); - withExposedPorts(8443); - withFixedExposedPort(8080, 8080); - withEnv("KEYCLOAK_USER", "admin"); - withEnv("KEYCLOAK_PASSWORD", "admin"); - withEnv("KEYCLOAK_HTTPS_PORT", "8443"); - withEnv("PROXY_ADDRESS_FORWARDING", "true"); - withEnv("KEYCLOAK_IMPORT", "/opt/jboss/keycloak/realms/kafka-authz-realm.json"); - waitingFor(Wait.forLogMessage(".*WFLYSRV0025.*", 1)); - withNetwork(Network.SHARED); - withNetworkAliases("keycloak"); - withCopyFileToContainer(MountableFile.forClasspathResource("keycloak/realms/kafka-authz-realm.json"), - "/opt/jboss/keycloak/realms/kafka-authz-realm.json"); - withCommand("-Dkeycloak.profile.feature.upload_scripts=enabled", "-b", "0.0.0.0"); - } - - public void createHostsFile() { - try (FileWriter fileWriter = new FileWriter("target/hosts")) { - String dockerHost = this.getHost(); - if ("localhost".equals(dockerHost)) { - fileWriter.write("127.0.0.1 keycloak"); - } else { - fileWriter.write(dockerHost + " keycloak"); - } - fileWriter.flush(); - } catch (Exception e) { - e.printStackTrace(); - } - } -} diff --git a/integration-tests/kafka-oauth/src/test/resources/kafkaServer.properties b/integration-tests/kafka-oauth/src/test/resources/kafkaServer.properties index efc0bb86d0..cbf18d7404 100644 --- a/integration-tests/kafka-oauth/src/test/resources/kafkaServer.properties +++ b/integration-tests/kafka-oauth/src/test/resources/kafkaServer.properties @@ -76,22 +76,11 @@ oauth.username.claim=preferred_username principal.builder.class=io.strimzi.kafka.oauth.server.OAuthKafkaPrincipalBuilder listener.name.jwt.sasl.enabled.mechanisms=OAUTHBEARER,PLAIN -listener.name.jwt.oauthbearer.sasl.jaas.config=org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required \ - oauth.jwks.endpoint.uri="http://keycloak:8080/auth/realms/kafka-authz/protocol/openid-connect/certs" \ - oauth.valid.issuer.uri="http://keycloak:8080/auth/realms/kafka-authz" \ - oauth.token.endpoint.uri="http://keycloak:8080/auth/realms/kafka-authz/protocol/openid-connect/token" \ - oauth.client.id="kafka" \ - oauth.client.secret="kafka-secret"; +listener.name.jwt.oauthbearer.sasl.jaas.config=set_by_test listener.name.jwt.oauthbearer.sasl.server.callback.handler.class=io.strimzi.kafka.oauth.server.JaasServerOauthValidatorCallbackHandler listener.name.jwt.oauthbearer.sasl.login.callback.handler.class=io.strimzi.kafka.oauth.client.JaasClientOauthLoginCallbackHandler -listener.name.jwt.plain.sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required \ - oauth.jwks.endpoint.uri="http://keycloak:8080/auth/realms/kafka-authz/protocol/openid-connect/certs" \ - oauth.valid.issuer.uri="http://keycloak:8080/auth/realms/kafka-authz" \ - oauth.token.endpoint.uri="http://keycloak:8080/auth/realms/kafka-authz/protocol/openid-connect/token" \ - oauth.client.id="kafka" \ - oauth.client.secret="kafka-secret" \ - unsecuredLoginStringClaim_sub="admin"; +#listener.name.jwt.plain.sasl.jaas.config=set_by_test listener.name.jwt.plain.sasl.server.callback.handler.class=io.strimzi.kafka.oauth.server.plain.JaasServerOauthOverPlainValidatorCallbackHandler diff --git a/pom.xml b/pom.xml index 23141de3d7..fae0b35589 100644 --- a/pom.xml +++ b/pom.xml @@ -230,6 +230,7 @@ <infinispan.container.image>docker.io/infinispan/server:14.0</infinispan.container.image> <influxdb.container.image>docker.io/influxdb:1.8.10</influxdb.container.image> <kafka.container.image>confluentinc/cp-kafka:7.4.0</kafka.container.image> + <keycloak.container.image>quay.io/keycloak/keycloak:22.0.5</keycloak.container.image> <kudu.container.image>docker.io/apache/kudu:1.15.0</kudu.container.image> <localstack.container.image>docker.io/localstack/localstack:2.2.0</localstack.container.image> <lra-coordinator.container.image>quay.io/jbosstm/lra-coordinator:7.0.0.Final-3.2.2.Final</lra-coordinator.container.image>