From f4bcf99d30e592fe8b5e4de7fb9d57b6941ce480 Mon Sep 17 00:00:00 2001 From: Eddu Melendez Date: Tue, 18 Apr 2023 12:29:43 +0100 Subject: [PATCH 1/2] Add service connection from Testcontainers Redpanda Redpanda (https://redpanda.com/) is Kafka-compatible and Testcontainers provides a module. This commit adds support for creating KafkaConnectionDetails from a @ServiceConnection-annotated RedpandaContainer. See gh-34780 --- .../spring-boot-testcontainers/build.gradle | 1 + ...andaContainerConnectionDetailsFactory.java | 65 +++++++++++++ .../connection/redpanda/package-info.java | 20 ++++ .../main/resources/META-INF/spring.factories | 3 +- ...nectionDetailsFactoryIntegrationTests.java | 93 +++++++++++++++++++ .../testcontainers/DockerImageNames.java | 11 +++ 6 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/redpanda/RedpandaContainerConnectionDetailsFactory.java create mode 100644 spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/redpanda/package-info.java create mode 100644 spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/redpanda/RedpandaContainerConnectionDetailsFactoryIntegrationTests.java diff --git a/spring-boot-project/spring-boot-testcontainers/build.gradle b/spring-boot-project/spring-boot-testcontainers/build.gradle index d7b70f7132..b8fdd467d7 100644 --- a/spring-boot-project/spring-boot-testcontainers/build.gradle +++ b/spring-boot-project/spring-boot-testcontainers/build.gradle @@ -27,6 +27,7 @@ dependencies { optional("org.testcontainers:neo4j") optional("org.testcontainers:postgresql") optional("org.testcontainers:rabbitmq") + optional("org.testcontainers:redpanda") optional("org.testcontainers:r2dbc") testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support")) diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/redpanda/RedpandaContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/redpanda/RedpandaContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..dd862197d5 --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/redpanda/RedpandaContainerConnectionDetailsFactory.java @@ -0,0 +1,65 @@ +/* + * Copyright 2012-2023 the original author or authors. + * + * 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 + * + * https://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.springframework.boot.testcontainers.service.connection.redpanda; + +import java.net.URI; +import java.util.List; + +import org.testcontainers.redpanda.RedpandaContainer; + +import org.springframework.boot.autoconfigure.kafka.KafkaConnectionDetails; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; + +/** + * {@link ContainerConnectionDetailsFactory} to create {@link KafkaConnectionDetails} from + * a {@link ServiceConnection @ServiceConnection}-annotated {@link RedpandaContainer}. + * + * @author Eddú Meléndez + */ +class RedpandaContainerConnectionDetailsFactory + extends ContainerConnectionDetailsFactory { + + @Override + protected KafkaConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource source) { + return new RedpandaContainerConnectionDetails(source); + } + + /** + * {@link KafkaConnectionDetails} backed by a {@link ContainerConnectionSource}. + */ + private static final class RedpandaContainerConnectionDetails extends ContainerConnectionDetails + implements KafkaConnectionDetails { + + private final RedpandaContainer container; + + private RedpandaContainerConnectionDetails(ContainerConnectionSource source) { + super(source); + this.container = source.getContainer(); + } + + @Override + public List getBootstrapNodes() { + URI uri = URI.create(this.container.getBootstrapServers()); + return List.of(new Node(uri.getHost(), uri.getPort())); + } + + } + +} diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/redpanda/package-info.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/redpanda/package-info.java new file mode 100644 index 0000000000..33a2a240c7 --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/redpanda/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2023 the original author or authors. + * + * 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 + * + * https://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. + */ + +/** + * Support for testcontainers Redpanda service connections. + */ +package org.springframework.boot.testcontainers.service.connection.redpanda; diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories index 25a61dcf17..68f7a17686 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories @@ -23,4 +23,5 @@ org.springframework.boot.testcontainers.service.connection.r2dbc.MariaDbR2dbcCon org.springframework.boot.testcontainers.service.connection.r2dbc.MsSqlServerR2dbcContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.r2dbc.MySqlR2dbcContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.r2dbc.PostgresR2dbcContainerConnectionDetailsFactory,\ -org.springframework.boot.testcontainers.service.connection.redis.RedisContainerConnectionDetailsFactory +org.springframework.boot.testcontainers.service.connection.redis.RedisContainerConnectionDetailsFactory,\ +org.springframework.boot.testcontainers.service.connection.redpanda.RedpandaContainerConnectionDetailsFactory diff --git a/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/redpanda/RedpandaContainerConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/redpanda/RedpandaContainerConnectionDetailsFactoryIntegrationTests.java new file mode 100644 index 0000000000..0d066f6205 --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/redpanda/RedpandaContainerConnectionDetailsFactoryIntegrationTests.java @@ -0,0 +1,93 @@ +/* + * Copyright 2012-2023 the original author or authors. + * + * 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 + * + * https://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.springframework.boot.testcontainers.service.connection.redpanda; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import org.awaitility.Awaitility; +import org.junit.jupiter.api.Test; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.redpanda.RedpandaContainer; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.boot.testsupport.testcontainers.DockerImageNames; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link RedpandaContainerConnectionDetailsFactory}. + * + * @author Eddú Meléndez + */ +@SpringJUnitConfig +@Testcontainers(disabledWithoutDocker = true) +@TestPropertySource(properties = { "spring.kafka.consumer.group-id=test-group", + "spring.kafka.consumer.auto-offset-reset=earliest" }) +class RedpandaContainerConnectionDetailsFactoryIntegrationTests { + + @Container + @ServiceConnection + static final RedpandaContainer redpanda = new RedpandaContainer(DockerImageNames.redpanda()); + + @Autowired + KafkaTemplate kafkaTemplate; + + @Autowired + TestListener listener; + + @Test + void connectionCanBeMadeToRedpandaContainer() { + this.kafkaTemplate.send("test-topic", "test-data"); + Awaitility.waitAtMost(Duration.ofSeconds(30)) + .untilAsserted(() -> assertThat(this.listener.messages).containsExactly("test-data")); + } + + @Configuration(proxyBeanMethods = false) + @ImportAutoConfiguration(KafkaAutoConfiguration.class) + static class TestConfiguration { + + @Bean + TestListener testListener() { + return new TestListener(); + } + + } + + static class TestListener { + + private final List messages = new ArrayList<>(); + + @KafkaListener(topics = "test-topic") + void processMessage(String message) { + this.messages.add(message); + } + + } + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java index cfbd54321a..e70f4b2303 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java @@ -22,6 +22,7 @@ import org.testcontainers.utility.DockerImageName; * Create {@link DockerImageName} instances for services used in integration tests. * * @author Stephane Nicoll + * @author Eddú Meléndez * @since 2.3.6 */ public final class DockerImageNames { @@ -46,6 +47,8 @@ public final class DockerImageNames { private static final String REDIS_VERSION = "4.0.14"; + private static final String REDPANDA_VERSION = "v23.1.2"; + private static final String REGISTRY_VERSION = "2.7.1"; private DockerImageNames() { @@ -131,6 +134,14 @@ public final class DockerImageNames { return DockerImageName.parse("redis").withTag(REDIS_VERSION); } + /** + * Return a {@link DockerImageName} suitable for running Redpanda. + * @return a docker image name for running redpanda + */ + public static DockerImageName redpanda() { + return DockerImageName.parse("docker.redpanda.com/redpandadata/redpanda").withTag(REDPANDA_VERSION); + } + /** * Return a {@link DockerImageName} suitable for running a Docker registry. * @return a docker image name for running a registry From 98d077c74c83f37f237518fc1aa2ad94ff893bda Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 18 Apr 2023 12:45:48 +0100 Subject: [PATCH 2/2] Polish "Add service connection from Testcontainers Redpanda" See gh-34780 --- .../spring-boot-docs/src/docs/asciidoc/features/testing.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc index fe7900384f..99b138f3d9 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc @@ -959,7 +959,7 @@ The following service connection factories are provided in the `spring-boot-test | Containers of type `JdbcDatabaseContainer` | `KafkaConnectionDetails` -| Containers of type `KafkaContainer` +| Containers of type `KafkaContainer` or `RedpandaContainer` | `LiquibaseConnectionDetails` | Containers of type `JdbcDatabaseContainer`