@@ -40,7 +40,7 @@ import org.apache.kafka.network.SocketServerConfigs
4040import org .apache .kafka .queue .KafkaEventQueue
4141import org .apache .kafka .raft .{MetadataLogConfig , QuorumConfig }
4242import org .apache .kafka .server .{ClientMetricsManager , ServerSocketFactory }
43- import org .apache .kafka .server .common .{MetadataVersion , TransactionVersion }
43+ import org .apache .kafka .server .common .{KRaftVersion , MetadataVersion , TransactionVersion }
4444import org .apache .kafka .server .config .{KRaftConfigs , ServerConfigs , ServerLogConfigs }
4545import org .apache .kafka .server .fault .{FaultHandler , MockFaultHandler }
4646import org .apache .kafka .server .util .timer .SystemTimer
@@ -50,6 +50,7 @@ import org.junit.jupiter.params.provider.Arguments
5050
5151import java .nio .file .{Files , Paths }
5252import scala .collection .Seq
53+ import scala .collection .mutable .{Map => MutableMap }
5354import scala .jdk .CollectionConverters ._
5455import scala .jdk .OptionConverters .RichOptional
5556
@@ -71,7 +72,12 @@ class KRaftQuorumImplementation(
7172 val controllerQuorumVotersFuture : CompletableFuture [util.Map [Integer , InetSocketAddress ]],
7273 val clusterId : String ,
7374 val log : Logging ,
74- val faultHandler : FaultHandler
75+ val faultHandler : FaultHandler ,
76+ // Add these new fields for bootstrap metadata creation
77+ val metadataVersion : MetadataVersion ,
78+ val featureLevels : MutableMap [String , Short ],
79+ val controllerListenerName : String ,
80+ val formatterSettingsApplier : Formatter => Unit // Function to apply test-specific formatter settings
7581) extends QuorumImplementation {
7682 override def createBroker (
7783 config : KafkaConfig ,
@@ -102,20 +108,75 @@ class KRaftQuorumImplementation(
102108 metaPropertiesEnsemble.verify(Optional .of(clusterId),
103109 OptionalInt .of(config.nodeId),
104110 util.EnumSet .of(REQUIRE_AT_LEAST_ONE_VALID , REQUIRE_METADATA_LOG_DIR ))
105- val sharedServer = new SharedServer (
106- config,
107- metaPropertiesEnsemble,
108- time,
109- new Metrics (),
110- controllerQuorumVotersFuture,
111- controllerQuorumVotersFuture.get().values(),
112- faultHandlerFactory,
113- ServerSocketFactory .INSTANCE ,
114- )
111+ // Create broker's own bootstrap checkpoint instead of copying from controller
112+ val brokerMetadataDir = new File (config.metadataLogDir, " __cluster_metadata-0" )
113+ brokerMetadataDir.mkdirs()
114+
115+ // Create broker's own bootstrap snapshot using formatter.run()
116+ // Use a temporary directory for the formatter since MetaPropertiesEnsemble already set up the real directory
117+ val tempFormatterDir = TestUtils .tempDir()
118+ val brokerFormatter = new Formatter ()
119+ brokerFormatter.setClusterId(clusterId)
120+ brokerFormatter.setNodeId(config.nodeId)
121+ brokerFormatter.addDirectory(tempFormatterDir.getAbsolutePath) // Use temp dir to avoid "already formatted" conflict
122+ brokerFormatter.setReleaseVersion(metadataVersion)
123+ brokerFormatter.setUnstableFeatureVersionsEnabled(true )
124+ brokerFormatter.setControllerListenerName(controllerListenerName)
125+ brokerFormatter.setMetadataLogDirectory(tempFormatterDir.getAbsolutePath) // Format in temp dir
126+ featureLevels.foreach { case (featureName, level) =>
127+ // Only set feature levels that aren't handled by other formatter methods
128+ // MetadataVersion.FEATURE_NAME is handled by setReleaseVersion
129+ // KRaftVersion.FEATURE_NAME is handled internally by the formatter
130+ if (featureName != MetadataVersion .FEATURE_NAME && featureName != KRaftVersion .FEATURE_NAME ) {
131+ brokerFormatter.setFeatureLevel(featureName, level)
132+ }
133+ }
134+
135+ // IMPORTANT: Add SCRAM credentials to broker formatter (same as controller)
136+ formatterSettingsApplier(brokerFormatter)
137+
138+ // Run the formatter to create the bootstrap snapshot in temp directory
139+ brokerFormatter.run()
140+
141+ // Copy the bootstrap snapshot from temp directory to broker's metadata directory
142+ val tempBootstrapSnapshot = new File (new File (tempFormatterDir, " __cluster_metadata-0" ), " 00000000000000000000-0000000000.checkpoint" )
143+ val brokerBootstrapSnapshot = new File (brokerMetadataDir, " 00000000000000000000-0000000000.checkpoint" )
144+ if (tempBootstrapSnapshot.exists()) {
145+ java.nio.file.Files .copy(
146+ tempBootstrapSnapshot.toPath,
147+ brokerBootstrapSnapshot.toPath,
148+ java.nio.file.StandardCopyOption .REPLACE_EXISTING
149+ )
150+ } else {
151+ throw new IllegalStateException (s " Temp bootstrap snapshot not found: ${tempBootstrapSnapshot.getAbsolutePath}" )
152+ }
153+
154+ // Verify bootstrap snapshot creation was successful
155+ val brokerSnapshot = new File (brokerMetadataDir, " 00000000000000000000-0000000000.checkpoint" )
156+ if (! brokerSnapshot.exists()) {
157+ throw new IllegalStateException (s " Bootstrap snapshot creation failed: ${brokerSnapshot.getAbsolutePath}" )
158+ }
159+
160+ val sharedServer = new SharedServer (
161+ config,
162+ metaPropertiesEnsemble,
163+ time,
164+ new Metrics (),
165+ controllerQuorumVotersFuture,
166+ controllerQuorumVotersFuture.get().values(),
167+ faultHandlerFactory,
168+ ServerSocketFactory .INSTANCE ,
169+ )
170+
171+ sharedServer.startForBroker()
172+
115173 var broker : BrokerServer = null
116174 try {
117175 broker = new BrokerServer (sharedServer)
118- if (startup) broker.startup()
176+
177+ if (startup) {
178+ broker.startup()
179+ }
119180 broker
120181 } catch {
121182 case e : Throwable => {
@@ -286,6 +347,12 @@ abstract class QuorumTestHarness extends Logging {
286347 } else TransactionVersion .TV_1 .featureLevel()
287348 formatter.setFeatureLevel(TransactionVersion .FEATURE_NAME , transactionVersion)
288349
350+ // Collect all feature levels that will be used for bootstrap metadata
351+ val featureLevels = MutableMap [String , Short ]()
352+ featureLevels.put(MetadataVersion .FEATURE_NAME , metadataVersion.featureLevel())
353+ featureLevels.put(KRaftVersion .FEATURE_NAME , KRaftVersion .KRAFT_VERSION_1 .featureLevel())
354+ featureLevels.put(TransactionVersion .FEATURE_NAME , transactionVersion)
355+
289356 addFormatterSettings(formatter)
290357 formatter.run()
291358 val bootstrapMetadata = formatter.bootstrapMetadata()
@@ -324,7 +391,11 @@ abstract class QuorumTestHarness extends Logging {
324391 )
325392 }
326393 })
394+
395+
327396 controllerServer.startup()
397+
398+
328399 } catch {
329400 case e : Throwable =>
330401 if (controllerServer != null ) CoreUtils .swallow(controllerServer.shutdown(), this )
@@ -338,7 +409,12 @@ abstract class QuorumTestHarness extends Logging {
338409 controllerQuorumVotersFuture,
339410 formatter.clusterId(),
340411 this ,
341- faultHandler
412+ faultHandler,
413+ // Add the new parameters
414+ metadataVersion,
415+ featureLevels,
416+ config.controllerListenerNames.get(0 ),
417+ addFormatterSettings // Pass the addFormatterSettings method as a function
342418 )
343419 }
344420
0 commit comments