@@ -48,12 +48,8 @@ public class KafkaContainer extends GenericContainer<KafkaContainer> {
4848
4949 private boolean kraftEnabled = false ;
5050
51- private String clusterId = DEFAULT_CLUSTER_ID ;
52-
5351 private static final String PROTOCOL_PREFIX = "TC" ;
5452
55- private final Set <Supplier <String >> listeners = new HashSet <>();
56-
5753 /**
5854 * @deprecated use {@link #KafkaContainer(DockerImageName)} instead
5955 */
@@ -73,38 +69,32 @@ public KafkaContainer(String confluentPlatformVersion) {
7369 public KafkaContainer (final DockerImageName dockerImageName ) {
7470 super (dockerImageName );
7571 dockerImageName .assertCompatibleWith (DEFAULT_IMAGE_NAME );
72+ }
7673
77- withEnv ("KAFKA_INTER_BROKER_LISTENER_NAME" , "BROKER" );
78-
79- withEnv ("KAFKA_BROKER_ID" , "1" );
80- withEnv ("KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR" , DEFAULT_INTERNAL_TOPIC_RF );
81- withEnv ("KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS" , DEFAULT_INTERNAL_TOPIC_RF );
82- withEnv ("KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR" , DEFAULT_INTERNAL_TOPIC_RF );
83- withEnv ("KAFKA_TRANSACTION_STATE_LOG_MIN_ISR" , DEFAULT_INTERNAL_TOPIC_RF );
84- withEnv ("KAFKA_LOG_FLUSH_INTERVAL_MESSAGES" , Long .MAX_VALUE + "" );
85- withEnv ("KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS" , "0" );
86-
87- withExposedPorts (KAFKA_PORT );
74+ @ Override
75+ KafkaContainerDef createContainerDef () {
76+ return new KafkaContainerDef ();
77+ }
8878
89- withCreateContainerCmdModifier (cmd -> {
90- cmd .withEntrypoint ("sh" );
91- });
92- withCommand ("-c" , "while [ ! -f " + STARTER_SCRIPT + " ]; do sleep 0.1; done; " + STARTER_SCRIPT );
79+ @ Override
80+ KafkaContainerDef getContainerDef () {
81+ return (KafkaContainerDef ) super .getContainerDef ();
9382 }
9483
9584 public KafkaContainer withEmbeddedZookeeper () {
9685 if (this .kraftEnabled ) {
9786 throw new IllegalStateException ("Cannot configure Zookeeper when using Kraft mode" );
9887 }
99- externalZookeeperConnect = null ;
88+ getContainerDef (). withEmbeddedZookeeper () ;
10089 return self ();
10190 }
10291
10392 public KafkaContainer withExternalZookeeper (String connectString ) {
10493 if (this .kraftEnabled ) {
10594 throw new IllegalStateException ("Cannot configure Zookeeper when using Kraft mode" );
10695 }
107- externalZookeeperConnect = connectString ;
96+ this .externalZookeeperConnect = connectString ;
97+ getContainerDef ().withZookeeper (connectString );
10898 return self ();
10999 }
110100
@@ -114,6 +104,7 @@ public KafkaContainer withKraft() {
114104 }
115105 verifyMinKraftVersion ();
116106 this .kraftEnabled = true ;
107+ getContainerDef ().withRaft ();
117108 return self ();
118109 }
119110
@@ -137,7 +128,7 @@ private boolean isLessThanCP740() {
137128
138129 public KafkaContainer withClusterId (String clusterId ) {
139130 Objects .requireNonNull (clusterId , "clusterId cannot be null" );
140- this . clusterId = clusterId ;
131+ getContainerDef (). withClusterId ( clusterId ) ;
141132 return self ();
142133 }
143134
@@ -147,77 +138,10 @@ public String getBootstrapServers() {
147138
148139 @ Override
149140 protected void configure () {
150- // Use two listeners with different names, it will force Kafka to communicate with itself via internal
151- // listener when KAFKA_INTER_BROKER_LISTENER_NAME is set, otherwise Kafka will try to use the advertised listener
152- Set <String > listeners = new HashSet <>();
153- listeners .add ("PLAINTEXT://0.0.0.0:" + KAFKA_PORT );
154- listeners .add ("BROKER://0.0.0.0:9092" );
155-
156- Set <String > listenerSecurityProtocolMap = new HashSet <>();
157- listenerSecurityProtocolMap .add ("BROKER:PLAINTEXT" );
158- listenerSecurityProtocolMap .add ("PLAINTEXT:PLAINTEXT" );
141+ getContainerDef ().resolveListeners ();
159142
160- List <Supplier <String >> listenersToTransform = new ArrayList <>(this .listeners );
161- for (int i = 0 ; i < listenersToTransform .size (); i ++) {
162- Supplier <String > listenerSupplier = listenersToTransform .get (i );
163- String protocol = String .format ("%s-%d" , PROTOCOL_PREFIX , i );
164- String listener = listenerSupplier .get ();
165- String listenerPort = listener .split (":" )[1 ];
166- String listenerProtocol = String .format ("%s://0.0.0.0:%s" , protocol , listenerPort );
167- String protocolMap = String .format ("%s:PLAINTEXT" , protocol );
168- listeners .add (listenerProtocol );
169- listenerSecurityProtocolMap .add (protocolMap );
170-
171- String host = listener .split (":" )[0 ];
172- withNetworkAliases (host );
173- }
174-
175- String kafkaListeners = String .join ("," , listeners );
176- String kafkaListenerSecurityProtocolMap = String .join ("," , listenerSecurityProtocolMap );
177-
178- withEnv ("KAFKA_LISTENERS" , kafkaListeners );
179- withEnv ("KAFKA_LISTENER_SECURITY_PROTOCOL_MAP" , kafkaListenerSecurityProtocolMap );
180-
181- if (this .kraftEnabled ) {
182- waitingFor (Wait .forLogMessage (".*Transitioning from RECOVERY to RUNNING.*" , 1 ));
183- configureKraft ();
184- } else {
185- waitingFor (Wait .forLogMessage (".*\\ [KafkaServer id=\\ d+\\ ] started.*" , 1 ));
186- configureZookeeper ();
187- }
188- }
189-
190- protected void configureKraft () {
191- //CP 7.4.0
192- getEnvMap ().computeIfAbsent ("CLUSTER_ID" , key -> clusterId );
193- getEnvMap ().computeIfAbsent ("KAFKA_NODE_ID" , key -> getEnvMap ().get ("KAFKA_BROKER_ID" ));
194- withEnv (
195- "KAFKA_LISTENER_SECURITY_PROTOCOL_MAP" ,
196- String .format ("%s,CONTROLLER:PLAINTEXT" , getEnvMap ().get ("KAFKA_LISTENER_SECURITY_PROTOCOL_MAP" ))
197- );
198- withEnv ("KAFKA_LISTENERS" , String .format ("%s,CONTROLLER://0.0.0.0:9094" , getEnvMap ().get ("KAFKA_LISTENERS" )));
199-
200- withEnv ("KAFKA_PROCESS_ROLES" , "broker,controller" );
201- getEnvMap ()
202- .computeIfAbsent (
203- "KAFKA_CONTROLLER_QUORUM_VOTERS" ,
204- key -> {
205- return String .format (
206- "%s@%s:9094" ,
207- getEnvMap ().get ("KAFKA_NODE_ID" ),
208- getNetwork () != null ? getNetworkAliases ().get (0 ) : "localhost"
209- );
210- }
211- );
212- withEnv ("KAFKA_CONTROLLER_LISTENER_NAMES" , "CONTROLLER" );
213- }
214-
215- protected void configureZookeeper () {
216- if (externalZookeeperConnect != null ) {
217- withEnv ("KAFKA_ZOOKEEPER_CONNECT" , externalZookeeperConnect );
218- } else {
219- addExposedPort (ZOOKEEPER_PORT );
220- withEnv ("KAFKA_ZOOKEEPER_CONNECT" , "localhost:" + ZOOKEEPER_PORT );
143+ if (!this .kraftEnabled && this .externalZookeeperConnect == null ) {
144+ getContainerDef ().withEmbeddedZookeeper ();
221145 }
222146 }
223147
@@ -229,7 +153,7 @@ protected void containerIsStarting(InspectContainerResponse containerInfo) {
229153 advertisedListeners .add (getBootstrapServers ());
230154 advertisedListeners .add (brokerAdvertisedListener (containerInfo ));
231155
232- List <Supplier <String >> listenersToTransform = new ArrayList <>(this .listeners );
156+ List <Supplier <String >> listenersToTransform = new ArrayList <>(getContainerDef () .listeners );
233157 for (int i = 0 ; i < listenersToTransform .size (); i ++) {
234158 Supplier <String > listenerSupplier = listenersToTransform .get (i );
235159 String protocol = String .format ("%s-%d" , PROTOCOL_PREFIX , i );
@@ -265,7 +189,7 @@ protected String commandKraft() {
265189 String command = "sed -i '/KAFKA_ZOOKEEPER_CONNECT/d' /etc/confluent/docker/configure\n " ;
266190 command +=
267191 "echo 'kafka-storage format --ignore-formatted -t \" " +
268- this . clusterId +
192+ getContainerDef (). getEnvVars (). get ( "CLUSTER_ID" ) +
269193 "\" -c /etc/kafka/kafka.properties' >> /etc/confluent/docker/configure\n " ;
270194 return command ;
271195 }
@@ -299,11 +223,113 @@ protected String commandZookeeper() {
299223 * @return this {@link KafkaContainer} instance
300224 */
301225 public KafkaContainer withListener (Supplier <String > listenerSupplier ) {
302- this . listeners . add (listenerSupplier );
226+ getContainerDef (). withListener (listenerSupplier );
303227 return this ;
304228 }
305229
306230 protected String brokerAdvertisedListener (InspectContainerResponse containerInfo ) {
307231 return String .format ("BROKER://%s:%s" , containerInfo .getConfig ().getHostName (), "9092" );
308232 }
233+
234+ private static class KafkaContainerDef extends ContainerDef {
235+
236+ private final Set <Supplier <String >> listeners = new HashSet <>();
237+
238+ private String clusterId = DEFAULT_CLUSTER_ID ;
239+
240+ KafkaContainerDef () {
241+ // Use two listeners with different names, it will force Kafka to communicate with itself via internal
242+ // listener when KAFKA_INTER_BROKER_LISTENER_NAME is set, otherwise Kafka will try to use the advertised listener
243+ addEnvVar ("KAFKA_LISTENERS" , "PLAINTEXT://0.0.0.0:" + KAFKA_PORT + ",BROKER://0.0.0.0:9092" );
244+ addEnvVar ("KAFKA_LISTENER_SECURITY_PROTOCOL_MAP" , "BROKER:PLAINTEXT,PLAINTEXT:PLAINTEXT" );
245+ addEnvVar ("KAFKA_INTER_BROKER_LISTENER_NAME" , "BROKER" );
246+
247+ addEnvVar ("KAFKA_BROKER_ID" , "1" );
248+ addEnvVar ("KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR" , DEFAULT_INTERNAL_TOPIC_RF );
249+ addEnvVar ("KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS" , DEFAULT_INTERNAL_TOPIC_RF );
250+ addEnvVar ("KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR" , DEFAULT_INTERNAL_TOPIC_RF );
251+ addEnvVar ("KAFKA_TRANSACTION_STATE_LOG_MIN_ISR" , DEFAULT_INTERNAL_TOPIC_RF );
252+ addEnvVar ("KAFKA_LOG_FLUSH_INTERVAL_MESSAGES" , Long .MAX_VALUE + "" );
253+ addEnvVar ("KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS" , "0" );
254+
255+ addExposedTcpPort (KAFKA_PORT );
256+
257+ setEntrypoint ("sh" );
258+ setCommand ("-c" , "while [ ! -f " + STARTER_SCRIPT + " ]; do sleep 0.1; done; " + STARTER_SCRIPT );
259+
260+ setWaitStrategy (Wait .forLogMessage (".*\\ [KafkaServer id=\\ d+\\ ] started.*" , 1 ));
261+ }
262+
263+ private void resolveListeners () {
264+ Set <String > additionalKafkaListeners = new HashSet <>();
265+ Set <String > additionalListenerSecurityProtocolMap = new HashSet <>();
266+
267+ List <Supplier <String >> listenersToTransform = new ArrayList <>(this .listeners );
268+ for (int i = 0 ; i < listenersToTransform .size (); i ++) {
269+ Supplier <String > listenerSupplier = listenersToTransform .get (i );
270+ String protocol = String .format ("%s-%d" , PROTOCOL_PREFIX , i );
271+ String listener = listenerSupplier .get ();
272+ String listenerPort = listener .split (":" )[1 ];
273+ String listenerProtocol = String .format ("%s://0.0.0.0:%s" , protocol , listenerPort );
274+ String protocolMap = String .format ("%s:PLAINTEXT" , protocol );
275+ additionalKafkaListeners .add (listenerProtocol );
276+ additionalListenerSecurityProtocolMap .add (protocolMap );
277+
278+ String host = listener .split (":" )[0 ];
279+ addNetworkAlias (host );
280+ }
281+
282+ String kafkaListeners = String .join ("," , additionalKafkaListeners );
283+ String kafkaListenerSecurityProtocolMap = String .join ("," , additionalListenerSecurityProtocolMap );
284+
285+ this .envVars .computeIfPresent ("KAFKA_LISTENERS" , (k , v ) -> String .join ("," , v , kafkaListeners ));
286+ this .envVars .computeIfPresent (
287+ "KAFKA_LISTENER_SECURITY_PROTOCOL_MAP" ,
288+ (k , v ) -> String .join ("," , v , kafkaListenerSecurityProtocolMap )
289+ );
290+ }
291+
292+ void withListener (Supplier <String > listenerSupplier ) {
293+ this .listeners .add (listenerSupplier );
294+ }
295+
296+ void withEmbeddedZookeeper () {
297+ addExposedTcpPort (ZOOKEEPER_PORT );
298+ addEnvVar ("KAFKA_ZOOKEEPER_CONNECT" , "localhost:" + ZOOKEEPER_PORT );
299+ }
300+
301+ void withZookeeper (String connectionString ) {
302+ addEnvVar ("KAFKA_ZOOKEEPER_CONNECT" , connectionString );
303+ }
304+
305+ void withClusterId (String clusterId ) {
306+ this .clusterId = clusterId ;
307+ }
308+
309+ void withRaft () {
310+ this .envVars .computeIfAbsent ("CLUSTER_ID" , key -> clusterId );
311+ this .envVars .computeIfAbsent ("KAFKA_NODE_ID" , key -> getEnvVars ().get ("KAFKA_BROKER_ID" ));
312+ addEnvVar (
313+ "KAFKA_LISTENER_SECURITY_PROTOCOL_MAP" ,
314+ String .format ("%s,CONTROLLER:PLAINTEXT" , getEnvVars ().get ("KAFKA_LISTENER_SECURITY_PROTOCOL_MAP" ))
315+ );
316+ addEnvVar (
317+ "KAFKA_LISTENERS" ,
318+ String .format ("%s,CONTROLLER://0.0.0.0:9094" , getEnvVars ().get ("KAFKA_LISTENERS" ))
319+ );
320+ addEnvVar ("KAFKA_PROCESS_ROLES" , "broker,controller" );
321+
322+ String firstNetworkAlias = getNetworkAliases ().stream ().findFirst ().orElse (null );
323+ String networkAlias = getNetwork () != null ? firstNetworkAlias : "localhost" ;
324+ String controllerQuorumVoters = String .format (
325+ "%s@%s:9094" ,
326+ getEnvVars ().get ("KAFKA_NODE_ID" ),
327+ networkAlias
328+ );
329+ this .envVars .computeIfAbsent ("KAFKA_CONTROLLER_QUORUM_VOTERS" , key -> controllerQuorumVoters );
330+ addEnvVar ("KAFKA_CONTROLLER_LISTENER_NAMES" , "CONTROLLER" );
331+
332+ setWaitStrategy (Wait .forLogMessage (".*Transitioning from RECOVERY to RUNNING.*" , 1 ));
333+ }
334+ }
309335}
0 commit comments