Skip to content

Commit 3b95f64

Browse files
authored
Merge branch 'main' into add-scylladb
2 parents 19ae30a + 847bb1a commit 3b95f64

File tree

27 files changed

+1064
-10
lines changed

27 files changed

+1064
-10
lines changed

.github/ISSUE_TEMPLATE/bug_report.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ body:
5050
- Oracle Free
5151
- Oracle XE
5252
- OrientDB
53+
- Pinecone
5354
- PostgreSQL
5455
- Presto
5556
- Pulsar

.github/ISSUE_TEMPLATE/enhancement.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ body:
5050
- Oracle Free
5151
- Oracle XE
5252
- OrientDB
53+
- Pinecone
5354
- PostgreSQL
5455
- Presto
5556
- Pulsar

.github/ISSUE_TEMPLATE/feature.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ body:
5050
- Oracle Free
5151
- Oracle XE
5252
- OrientDB
53+
- Pinecone
5354
- PostgreSQL
5455
- Qdrant
5556
- QuestDB

.github/dependabot.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,11 @@ updates:
274274
schedule:
275275
interval: "weekly"
276276
open-pull-requests-limit: 10
277+
- package-ecosystem: "gradle"
278+
directory: "/modules/pinecone"
279+
schedule:
280+
interval: "weekly"
281+
open-pull-requests-limit: 10
277282
- package-ecosystem: "gradle"
278283
directory: "/modules/pulsar"
279284
schedule:

.github/labeler.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@
168168
- changed-files:
169169
- any-glob-to-any-file:
170170
- modules/orientdb/**/*
171+
"modules/pinecone":
172+
- changed-files:
173+
- any-glob-to-any-file:
174+
- modules/pinecone/**/*
171175
"modules/postgres":
172176
- changed-files:
173177
- any-glob-to-any-file:

.github/settings.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,9 @@ labels:
211211
- name: modules/orientdb
212212
color: '#006b75'
213213

214+
- name: modules/pinecone
215+
color: '#006b75'
216+
214217
- name: modules/postgres
215218
color: '#006b75'
216219

docs/modules/azure.md

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ This module is INCUBATING. While it is ready for use and operational in the curr
55

66
Testcontainers module for the Microsoft Azure's [SDK](https://github.com/Azure/azure-sdk-for-java).
77

8-
Currently, the module supports `Azurite` and `CosmosDB` emulators. In order to use them, you should use the following classes:
8+
Currently, the module supports `Azurite`, `Azure Event Hubs`, `Azure Service Bus` and `CosmosDB` emulators. In order to use them, you should use the following classes:
99

1010
Class | Container Image
1111
-|-
1212
AzuriteContainer | [mcr.microsoft.com/azure-storage/azurite](https://github.com/microsoft/containerregistry)
13+
AzureEventHubsContainer | [mcr.microsoft.com/azure-messaging/eventhubs-emulator](https://github.com/microsoft/containerregistry)
14+
AzureServiceBusEmulatorContainer | [mcr.microsoft.com/azure-messaging/servicebus-emulator](https://github.com/microsoft/containerregistry)
15+
AzureServiceBusContainer | [mcr.microsoft.com/azure-messaging/servicebus-emulator](https://github.com/microsoft/containerregistry)
1316
CosmosDBEmulatorContainer | [mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator](https://github.com/microsoft/containerregistry)
1417

1518
## Usage example
@@ -72,6 +75,66 @@ Build Azure Table client:
7275
!!! note
7376
We can use custom credentials the same way as defined in the Blob section.
7477

78+
### Azure Event Hubs Emulator
79+
80+
<!--codeinclude-->
81+
[Configuring the Azure Event Hubs Emulator container](../../modules/azure/src/test/resources/eventhubs_config.json)
82+
<!--/codeinclude-->
83+
84+
Start Azure Event Hubs Emulator during a test:
85+
86+
<!--codeinclude-->
87+
[Setting up a network](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventHubsContainerTest.java) inside_block:network
88+
<!--/codeinclude-->
89+
90+
<!--codeinclude-->
91+
[Starting an Azurite container as dependency](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventHubsContainerTest.java) inside_block:azuriteContainer
92+
<!--/codeinclude-->
93+
94+
<!--codeinclude-->
95+
[Starting an Azure Event Hubs Emulator container](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventHubsContainerTest.java) inside_block:emulatorContainer
96+
<!--/codeinclude-->
97+
98+
#### Using Azure Event Hubs clients
99+
100+
Configure the consumer and the producer clients:
101+
102+
<!--codeinclude-->
103+
[Configuring the clients](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventHubsContainerTest.java) inside_block:createProducerAndConsumer
104+
<!--/codeinclude-->
105+
106+
### Azure Service Bus Emulator
107+
108+
<!--codeinclude-->
109+
[Configuring the Azure Service Bus Emulator container](../../modules/azure/src/test/resources/service-bus-config.json)
110+
<!--/codeinclude-->
111+
112+
Start Azure Service Bus Emulator during a test:
113+
114+
<!--codeinclude-->
115+
[Setting up a network](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusContainerTest.java) inside_block:network
116+
<!--/codeinclude-->
117+
118+
<!--codeinclude-->
119+
[Starting a SQL Server container as dependency](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusContainerTest.java) inside_block:sqlContainer
120+
<!--/codeinclude-->
121+
122+
<!--codeinclude-->
123+
[Starting a Service Bus Emulator container](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusContainerTest.java) inside_block:emulatorContainer
124+
<!--/codeinclude-->
125+
126+
#### Using Azure Service Bus clients
127+
128+
Configure the sender and the processor clients:
129+
130+
<!--codeinclude-->
131+
[Configuring the sender client](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusContainerTest.java) inside_block:senderClient
132+
<!--/codeinclude-->
133+
134+
<!--codeinclude-->
135+
[Configuring the processor client](../../modules/azure/src/test/java/org/testcontainers/azure/AzureServiceBusContainerTest.java) inside_block:processorClient
136+
<!--/codeinclude-->
137+
75138
### CosmosDB
76139

77140
Start Azure CosmosDB Emulator during a test:

docs/modules/pinecone.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Pinecone
2+
3+
Testcontainers module for [Pinecone](https://github.com/orgs/pinecone-io/packages/container/package/pinecone-local).
4+
5+
## PineconeContainer's usage examples
6+
7+
You can start an Pinecone container instance from any Java application by using:
8+
9+
<!--codeinclude-->
10+
[Pinecone container](../../modules/pinecone/src/test/java/org/testcontainers/pinecone/PineconeContainerTest.java) inside_block:container
11+
<!--/codeinclude-->
12+
13+
## Adding this module to your project dependencies
14+
15+
Add the following dependency to your `pom.xml`/`build.gradle` file:
16+
17+
=== "Gradle"
18+
```groovy
19+
testImplementation "org.testcontainers:pinecone:{{latest_version}}"
20+
```
21+
22+
=== "Maven"
23+
```xml
24+
<dependency>
25+
<groupId>org.testcontainers</groupId>
26+
<artifactId>pinecone</artifactId>
27+
<version>{{latest_version}}</version>
28+
<scope>test</scope>
29+
</dependency>
30+
```
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package com.example.kafkacluster;
2+
3+
import org.apache.kafka.common.Uuid;
4+
import org.awaitility.Awaitility;
5+
import org.testcontainers.containers.Container;
6+
import org.testcontainers.containers.GenericContainer;
7+
import org.testcontainers.containers.Network;
8+
import org.testcontainers.kafka.KafkaContainer;
9+
import org.testcontainers.lifecycle.Startable;
10+
import org.testcontainers.utility.DockerImageName;
11+
12+
import java.time.Duration;
13+
import java.util.Collection;
14+
import java.util.stream.Collectors;
15+
import java.util.stream.IntStream;
16+
17+
import static org.assertj.core.api.Assertions.assertThat;
18+
19+
public class ApacheKafkaContainerCluster implements Startable {
20+
21+
private final int brokersNum;
22+
23+
private final Network network;
24+
25+
private final Collection<KafkaContainer> brokers;
26+
27+
public ApacheKafkaContainerCluster(String version, int brokersNum, int internalTopicsRf) {
28+
if (brokersNum < 0) {
29+
throw new IllegalArgumentException("brokersNum '" + brokersNum + "' must be greater than 0");
30+
}
31+
if (internalTopicsRf < 0 || internalTopicsRf > brokersNum) {
32+
throw new IllegalArgumentException(
33+
"internalTopicsRf '" + internalTopicsRf + "' must be less than brokersNum and greater than 0"
34+
);
35+
}
36+
37+
this.brokersNum = brokersNum;
38+
this.network = Network.newNetwork();
39+
40+
String controllerQuorumVoters = IntStream
41+
.range(0, brokersNum)
42+
.mapToObj(brokerNum -> String.format("%d@broker-%d:9094", brokerNum, brokerNum))
43+
.collect(Collectors.joining(","));
44+
45+
String clusterId = Uuid.randomUuid().toString();
46+
47+
this.brokers =
48+
IntStream
49+
.range(0, brokersNum)
50+
.mapToObj(brokerNum -> {
51+
return new KafkaContainer(DockerImageName.parse("apache/kafka").withTag(version))
52+
.withNetwork(this.network)
53+
.withNetworkAliases("broker-" + brokerNum)
54+
.withEnv("CLUSTER_ID", clusterId)
55+
.withEnv("KAFKA_BROKER_ID", brokerNum + "")
56+
.withEnv("KAFKA_NODE_ID", brokerNum + "")
57+
.withEnv("KAFKA_CONTROLLER_QUORUM_VOTERS", controllerQuorumVoters)
58+
.withEnv("KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR", internalTopicsRf + "")
59+
.withEnv("KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS", "0")
60+
.withEnv("KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS", internalTopicsRf + "")
61+
.withEnv("KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR", internalTopicsRf + "")
62+
.withEnv("KAFKA_TRANSACTION_STATE_LOG_MIN_ISR", internalTopicsRf + "")
63+
.withStartupTimeout(Duration.ofMinutes(1));
64+
})
65+
.collect(Collectors.toList());
66+
}
67+
68+
public Collection<KafkaContainer> getBrokers() {
69+
return this.brokers;
70+
}
71+
72+
public String getBootstrapServers() {
73+
return brokers.stream().map(KafkaContainer::getBootstrapServers).collect(Collectors.joining(","));
74+
}
75+
76+
@Override
77+
public void start() {
78+
// Needs to start all the brokers at once
79+
brokers.parallelStream().forEach(GenericContainer::start);
80+
81+
Awaitility
82+
.await()
83+
.atMost(Duration.ofSeconds(30))
84+
.untilAsserted(() -> {
85+
Container.ExecResult result =
86+
this.brokers.stream()
87+
.findFirst()
88+
.get()
89+
.execInContainer(
90+
"sh",
91+
"-c",
92+
"/opt/kafka/bin/kafka-log-dirs.sh --bootstrap-server localhost:9093 --describe | grep -o '\"broker\"' | wc -l"
93+
);
94+
String brokers = result.getStdout().replace("\n", "");
95+
96+
assertThat(brokers).asInt().isEqualTo(this.brokersNum);
97+
});
98+
}
99+
100+
@Override
101+
public void stop() {
102+
this.brokers.stream().parallel().forEach(GenericContainer::stop);
103+
}
104+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package com.example.kafkacluster;
2+
3+
import com.google.common.collect.ImmutableMap;
4+
import org.apache.kafka.clients.admin.AdminClient;
5+
import org.apache.kafka.clients.admin.AdminClientConfig;
6+
import org.apache.kafka.clients.admin.NewTopic;
7+
import org.apache.kafka.clients.consumer.ConsumerConfig;
8+
import org.apache.kafka.clients.consumer.ConsumerRecord;
9+
import org.apache.kafka.clients.consumer.ConsumerRecords;
10+
import org.apache.kafka.clients.consumer.KafkaConsumer;
11+
import org.apache.kafka.clients.producer.KafkaProducer;
12+
import org.apache.kafka.clients.producer.ProducerConfig;
13+
import org.apache.kafka.clients.producer.ProducerRecord;
14+
import org.apache.kafka.common.serialization.StringDeserializer;
15+
import org.apache.kafka.common.serialization.StringSerializer;
16+
import org.awaitility.Awaitility;
17+
import org.junit.jupiter.api.Test;
18+
19+
import java.time.Duration;
20+
import java.util.Collection;
21+
import java.util.Collections;
22+
import java.util.UUID;
23+
import java.util.concurrent.TimeUnit;
24+
25+
import static org.assertj.core.api.Assertions.assertThat;
26+
import static org.assertj.core.api.Assertions.tuple;
27+
28+
class ApacheKafkaContainerClusterTest {
29+
30+
@Test
31+
void testKafkaContainerCluster() throws Exception {
32+
try (ApacheKafkaContainerCluster cluster = new ApacheKafkaContainerCluster("3.8.0", 3, 2)) {
33+
cluster.start();
34+
String bootstrapServers = cluster.getBootstrapServers();
35+
36+
assertThat(cluster.getBrokers()).hasSize(3);
37+
38+
testKafkaFunctionality(bootstrapServers, 3, 2);
39+
}
40+
}
41+
42+
protected void testKafkaFunctionality(String bootstrapServers, int partitions, int rf) throws Exception {
43+
try (
44+
AdminClient adminClient = AdminClient.create(
45+
ImmutableMap.of(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers)
46+
);
47+
KafkaProducer<String, String> producer = new KafkaProducer<>(
48+
ImmutableMap.of(
49+
ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,
50+
bootstrapServers,
51+
ProducerConfig.CLIENT_ID_CONFIG,
52+
UUID.randomUUID().toString()
53+
),
54+
new StringSerializer(),
55+
new StringSerializer()
56+
);
57+
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(
58+
ImmutableMap.of(
59+
ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,
60+
bootstrapServers,
61+
ConsumerConfig.GROUP_ID_CONFIG,
62+
"tc-" + UUID.randomUUID(),
63+
ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,
64+
"earliest"
65+
),
66+
new StringDeserializer(),
67+
new StringDeserializer()
68+
);
69+
) {
70+
String topicName = "messages";
71+
72+
Collection<NewTopic> topics = Collections.singletonList(new NewTopic(topicName, partitions, (short) rf));
73+
adminClient.createTopics(topics).all().get(30, TimeUnit.SECONDS);
74+
75+
consumer.subscribe(Collections.singletonList(topicName));
76+
77+
producer.send(new ProducerRecord<>(topicName, "testcontainers", "rulezzz")).get();
78+
79+
Awaitility
80+
.await()
81+
.atMost(Duration.ofSeconds(10))
82+
.untilAsserted(() -> {
83+
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
84+
85+
assertThat(records)
86+
.hasSize(1)
87+
.extracting(ConsumerRecord::topic, ConsumerRecord::key, ConsumerRecord::value)
88+
.containsExactly(tuple(topicName, "testcontainers", "rulezzz"));
89+
});
90+
91+
consumer.unsubscribe();
92+
}
93+
}
94+
}

0 commit comments

Comments
 (0)