Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KAFKA-17636 Fix scram bootstrap records #17305

Merged
merged 1 commit into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions core/src/main/scala/kafka/tools/StorageTool.scala
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ object StorageTool extends Logging {
if (namespace.getBoolean("standalone")) {
formatter.setInitialVoters(createStandaloneDynamicVoters(config))
}
Option(namespace.getList("add_scram")).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to double-check this since the no-scram case is actually a null rather than an empty list... 🫤

foreach(scramArgs => formatter.setScramArguments(scramArgs.asInstanceOf[util.List[String]]))
configToLogDirectories(config).foreach(formatter.addDirectory(_))
formatter.run()
}
Expand Down
50 changes: 49 additions & 1 deletion core/src/test/scala/unit/kafka/tools/StorageToolTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ import java.io.{ByteArrayOutputStream, File, PrintStream}
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.util
import java.util.Properties
import java.util.{Optional, Properties}
import kafka.server.KafkaConfig
import kafka.utils.TestUtils
import net.sourceforge.argparse4j.inf.ArgumentParserException
import org.apache.kafka.common.metadata.UserScramCredentialRecord
import org.apache.kafka.common.utils.Utils
import org.apache.kafka.server.common.{Features, MetadataVersion}
import org.apache.kafka.metadata.bootstrap.BootstrapDirectory
import org.apache.kafka.metadata.properties.{MetaPropertiesEnsemble, PropertiesUtils}
import org.apache.kafka.metadata.storage.FormatterException
import org.apache.kafka.raft.QuorumConfig
Expand All @@ -37,6 +39,7 @@ import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource

import scala.collection.mutable.ListBuffer
import scala.jdk.CollectionConverters.IterableHasAsScala

@Timeout(value = 40)
class StorageToolTest {
Expand Down Expand Up @@ -619,4 +622,49 @@ Found problem:

assertEquals("Invalid version format: invalid for feature metadata.version", exception.getMessage)
}

@Test
def testBootstrapScramRecords(): Unit = {
val availableDirs = Seq(TestUtils.tempDir())
val properties = new Properties()
properties.putAll(defaultDynamicQuorumProperties)
properties.setProperty("log.dirs", availableDirs.mkString(","))
val stream = new ByteArrayOutputStream()
val arguments = ListBuffer[String](
"--release-version", "3.9-IV0",
"--add-scram", "SCRAM-SHA-512=[name=alice,password=changeit]",
"--add-scram", "SCRAM-SHA-512=[name=bob,password=changeit]"
)

assertEquals(0, runFormatCommand(stream, properties, arguments.toSeq))

// Not doing full SCRAM record validation since that's covered elsewhere.
// Just checking that we generate the correct number of records
val bootstrapMetadata = new BootstrapDirectory(availableDirs.head.toString, Optional.empty).read
val scramRecords = bootstrapMetadata.records().asScala
.filter(apiMessageAndVersion => apiMessageAndVersion.message().isInstanceOf[UserScramCredentialRecord])
.map(apiMessageAndVersion => apiMessageAndVersion.message().asInstanceOf[UserScramCredentialRecord])
.toList
assertEquals(2, scramRecords.size)
assertEquals("alice", scramRecords.head.name())
assertEquals("bob", scramRecords.last.name())
}

@Test
def testScramRecordsOldReleaseVersion(): Unit = {
val availableDirs = Seq(TestUtils.tempDir())
val properties = new Properties()
properties.putAll(defaultDynamicQuorumProperties)
properties.setProperty("log.dirs", availableDirs.mkString(","))
val stream = new ByteArrayOutputStream()
val arguments = ListBuffer[String](
"--release-version", "3.4",
"--add-scram", "SCRAM-SHA-512=[name=alice,password=changeit]",
"--add-scram", "SCRAM-SHA-512=[name=bob,password=changeit]"
)

assertEquals(
"SCRAM is only supported in metadata.version 3.5-IV2 or later.",
assertThrows(classOf[FormatterException], () => runFormatCommand(stream, properties, arguments.toSeq)).getMessage)
}
}