Skip to content
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
35 changes: 26 additions & 9 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ ext {

defaultMaxHeapSize = "2g"
defaultJvmArgs = ["-Xss4m", "-XX:+UseParallelGC"]
if (JavaVersion.current() == JavaVersion.VERSION_16)
defaultJvmArgs.add("--illegal-access=permit")

userMaxForks = project.hasProperty('maxParallelForks') ? maxParallelForks.toInteger() : null
userIgnoreFailures = project.hasProperty('ignoreFailures') ? ignoreFailures : false
Expand Down Expand Up @@ -378,6 +380,27 @@ subprojects {
}
}

// The suites are for running sets of tests in IDEs.
// Gradle will run each test class, so we exclude the suites to avoid redundantly running the tests twice.
def testsToExclude = ['**/*Suite.class']
// Exclude PowerMock tests when running with Java 16 until a version of PowerMock that supports Java 16 is released
// The relevant issues are https://github.com/powermock/powermock/issues/1094 and https://github.com/powermock/powermock/issues/1099
if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_16)) {
testsToExclude.addAll([
// connect tests
"**/AbstractHerderTest.*", "**/ConnectClusterStateImplTest.*", "**/ConnectorPluginsResourceTest.*",
"**/ConnectorsResourceTest.*", "**/DistributedHerderTest.*", "**/FileOffsetBakingStoreTest.*",
"**/ErrorHandlingTaskTest.*", "**/KafkaConfigBackingStoreTest.*", "**/KafkaOffsetBackingStoreTest.*",
"**/KafkaBasedLogTest.*", "**/OffsetStorageWriterTest.*", "**/StandaloneHerderTest.*",
"**/SourceTaskOffsetCommitterTest.*", "**/WorkerConfigTransformerTest.*", "**/WorkerGroupMemberTest.*",
"**/WorkerSinkTaskTest.*", "**/WorkerSinkTaskThreadedTest.*", "**/WorkerSourceTaskTest.*",
"**/WorkerTaskTest.*", "**/WorkerTest.*", "**/RestServerTest.*",
// streams tests
"**/KafkaStreamsTest.*", "**/RepartitionTopicsTest.*", "**/RocksDBMetricsRecorderTest.*",
"**/StreamsMetricsImplTest.*", "**/StateManagerUtilTest.*", "**/TableSourceNodeTest.*"
])
}

test {
maxParallelForks = userMaxForks ?: Runtime.runtime.availableProcessors()
ignoreFailures = userIgnoreFailures
Expand All @@ -392,9 +415,7 @@ subprojects {
}
logTestStdout.rehydrate(delegate, owner, this)()

// The suites are for running sets of tests in IDEs.
// Gradle will run each test class, so we exclude the suites to avoid redundantly running the tests twice.
exclude '**/*Suite.class'
exclude testsToExclude

if (shouldUseJUnit5)
useJUnitPlatform()
Expand All @@ -420,9 +441,7 @@ subprojects {
}
logTestStdout.rehydrate(delegate, owner, this)()

// The suites are for running sets of tests in IDEs.
// Gradle will run each test class, so we exclude the suites to avoid redundantly running the tests twice.
exclude '**/*Suite.class'
exclude testsToExclude

if (shouldUseJUnit5) {
useJUnitPlatform {
Expand Down Expand Up @@ -454,9 +473,7 @@ subprojects {
}
logTestStdout.rehydrate(delegate, owner, this)()

// The suites are for running sets of tests in IDEs.
// Gradle will run each test class, so we exclude the suites to avoid redundantly running the tests twice.
exclude '**/*Suite.class'
exclude testsToExclude

if (shouldUseJUnit5) {
useJUnitPlatform {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ private void updateConnectionSetupTimeout(NodeConnectionState nodeState) {
*/
public void remove(String id) {
nodeState.remove(id);
connectingNodes.remove(id);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,15 @@ public int hashCode() {
return result;
}

/**
* Override toString to redact sensitive value.
* WARNING, user should be responsible to set the correct "isSensitive" field for each config entry.
*/
@Override
public String toString() {
return "ConfigEntry(" +
"name=" + name +
", value=" + value +
", value=" + (isSensitive ? "Redacted" : value) +
", source=" + source +
", isSensitive=" + isSensitive +
", isReadOnly=" + isReadOnly +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package org.apache.kafka.common.requests;

import org.apache.kafka.common.Uuid;
import org.apache.kafka.common.errors.UnsupportedVersionException;
import org.apache.kafka.common.message.MetadataRequestData;
import org.apache.kafka.common.message.MetadataRequestData.MetadataRequestTopic;
Expand Down Expand Up @@ -92,6 +93,16 @@ public MetadataRequest build(short version) {
if (!data.allowAutoTopicCreation() && version < 4)
throw new UnsupportedVersionException("MetadataRequest versions older than 4 don't support the " +
"allowAutoTopicCreation field");
if (data.topics() != null) {
data.topics().forEach(topic -> {
if (topic.name() == null)
throw new UnsupportedVersionException("MetadataRequest version " + version +
" does not support null topic names.");
if (topic.topicId() != Uuid.ZERO_UUID)
throw new UnsupportedVersionException("MetadataRequest version " + version +
" does not support non-zero topic IDs.");
});
}
return new MetadataRequest(data, version);
}

Expand All @@ -117,13 +128,17 @@ public MetadataRequestData data() {
public AbstractResponse getErrorResponse(int throttleTimeMs, Throwable e) {
Errors error = Errors.forException(e);
MetadataResponseData responseData = new MetadataResponseData();
if (topics() != null) {
for (String topic : topics())
if (data.topics() != null) {
for (MetadataRequestTopic topic : data.topics()) {
// the response does not allow null, so convert to empty string if necessary
String topicName = topic.name() == null ? "" : topic.name();
responseData.topics().add(new MetadataResponseData.MetadataResponseTopic()
.setName(topic)
.setName(topicName)
.setTopicId(topic.topicId())
.setErrorCode(error.code())
.setIsInternal(false)
.setPartitions(Collections.emptyList()));
}
}

responseData.setThrottleTimeMs(throttleTimeMs);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
//
// Version 9 is the first flexible version.
//
// Version 10 adds topicId.
// Version 10 adds topicId and allows name field to be null. However, this functionality was not implemented on the server.
// Versions 10 and 11 should not use the topicId field or set topic name to null.
//
// Version 11 deprecates IncludeClusterAuthorizedOperations field. This is now exposed
// by the DescribeCluster API (KIP-700).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,39 @@ public void testFailedConnectionToFirstAddressAfterReconnect() {
assertEquals(2, mockHostResolver.resolutionCount());
}

@Test
public void testCloseConnectingNode() {
Cluster cluster = TestUtils.clusterWith(2);
Node node0 = cluster.nodeById(0);
Node node1 = cluster.nodeById(1);
client.ready(node0, time.milliseconds());
selector.serverConnectionBlocked(node0.idString());
client.poll(1, time.milliseconds());
client.close(node0.idString());

// Poll without any connections should return without exceptions
client.poll(0, time.milliseconds());
assertFalse(NetworkClientUtils.isReady(client, node0, time.milliseconds()));
assertFalse(NetworkClientUtils.isReady(client, node1, time.milliseconds()));

// Connection to new node should work
client.ready(node1, time.milliseconds());
ByteBuffer buffer = RequestTestUtils.serializeResponseWithHeader(defaultApiVersionsResponse(), ApiKeys.API_VERSIONS.latestVersion(), 0);
selector.delayedReceive(new DelayedReceive(node1.idString(), new NetworkReceive(node1.idString(), buffer)));
while (!client.ready(node1, time.milliseconds()))
client.poll(1, time.milliseconds());
assertTrue(client.isReady(node1, time.milliseconds()));
selector.clear();

// New connection to node closed earlier should work
client.ready(node0, time.milliseconds());
buffer = RequestTestUtils.serializeResponseWithHeader(defaultApiVersionsResponse(), ApiKeys.API_VERSIONS.latestVersion(), 1);
selector.delayedReceive(new DelayedReceive(node0.idString(), new NetworkReceive(node0.idString(), buffer)));
while (!client.ready(node0, time.milliseconds()))
client.poll(1, time.milliseconds());
assertTrue(client.isReady(node0, time.milliseconds()));
}

private RequestHeader parseHeader(ByteBuffer buffer) {
buffer.getInt(); // skip size
return RequestHeader.parse(buffer.slice());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
import org.apache.kafka.test.TestSslUtils;
import org.apache.kafka.test.TestUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.condition.DisabledOnJre;
import org.junit.jupiter.api.condition.JRE;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
Expand Down Expand Up @@ -591,7 +593,7 @@ public void testInvalidKeyPassword(Args args) throws Exception {
}

/**
* Tests that connection success with the default TLS version.
* Tests that connection succeeds with the default TLS version.
*/
@ParameterizedTest
@ArgumentsSource(SslTransportLayerArgumentsProvider.class)
Expand All @@ -611,12 +613,6 @@ public void testTlsDefaults(Args args) throws Exception {
NetworkTestUtils.checkClientConnection(selector, "0", 10, 100);
server.verifyAuthenticationMetrics(1, 0);
selector.close();

checkAuthenticationFailed(args, "1", "TLSv1.1");
server.verifyAuthenticationMetrics(1, 1);

checkAuthenticationFailed(args, "2", "TLSv1");
server.verifyAuthenticationMetrics(1, 2);
}

/** Checks connection failed using the specified {@code tlsVersion}. */
Expand All @@ -636,12 +632,15 @@ private void checkAuthenticationFailed(Args args, String node, String tlsVersion
*/
@ParameterizedTest
@ArgumentsSource(SslTransportLayerArgumentsProvider.class)
public void testUnsupportedTLSVersion(Args args) throws Exception {
args.sslServerConfigs.put(SslConfigs.SSL_ENABLED_PROTOCOLS_CONFIG, Arrays.asList("TLSv1.2"));
@DisabledOnJre(JRE.JAVA_16)
public void testUnsupportedTlsVersion(Args args) throws Exception {
server = createEchoServer(args, SecurityProtocol.SSL);

checkAuthenticationFailed(args, "0", "TLSv1.1");
server.verifyAuthenticationMetrics(0, 1);

checkAuthenticationFailed(args, "0", "TLSv1");
server.verifyAuthenticationMetrics(0, 2);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,21 @@
*/
package org.apache.kafka.common.requests;

import org.apache.kafka.common.Uuid;
import org.apache.kafka.common.errors.UnsupportedVersionException;
import org.apache.kafka.common.message.MetadataRequestData;
import org.apache.kafka.common.protocol.ApiKeys;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class MetadataRequestTest {

Expand Down Expand Up @@ -65,4 +70,24 @@ public void testMetadataRequestVersion() {
assertEquals(minVersion, builder3.oldestAllowedVersion());
assertEquals(maxVersion, builder3.latestAllowedVersion());
}

@Test
public void testTopicIdAndNullTopicNameRequests() {
// Construct invalid MetadataRequestTopics. We will build each one separately and ensure the error is thrown.
List<MetadataRequestData.MetadataRequestTopic> topics = Arrays.asList(
new MetadataRequestData.MetadataRequestTopic().setName(null).setTopicId(Uuid.randomUuid()),
new MetadataRequestData.MetadataRequestTopic().setName(null),
new MetadataRequestData.MetadataRequestTopic().setTopicId(Uuid.randomUuid()),
new MetadataRequestData.MetadataRequestTopic().setName("topic").setTopicId(Uuid.randomUuid()));

// if version is 10 or 11, the invalid topic metadata should return an error
List<Short> invalidVersions = Arrays.asList((short) 10, (short) 11);
invalidVersions.forEach(version ->
topics.forEach(topic -> {
MetadataRequestData metadataRequestData = new MetadataRequestData().setTopics(Collections.singletonList(topic));
MetadataRequest.Builder builder = new MetadataRequest.Builder(metadataRequestData);
assertThrows(UnsupportedVersionException.class, () -> builder.build(version));
})
);
}
}
9 changes: 5 additions & 4 deletions core/src/main/scala/kafka/server/DynamicBrokerConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ class DynamicBrokerConfig(private val kafkaConfig: KafkaConfig) extends Logging
dynamicBrokerConfigs ++= props.asScala
updateCurrentConfig()
} catch {
case e: Exception => error(s"Per-broker configs of $brokerId could not be applied: $persistentProps", e)
case e: Exception => error(s"Per-broker configs of $brokerId could not be applied: ${persistentProps.keys()}", e)
}
}

Expand All @@ -306,7 +306,7 @@ class DynamicBrokerConfig(private val kafkaConfig: KafkaConfig) extends Logging
dynamicDefaultConfigs ++= props.asScala
updateCurrentConfig()
} catch {
case e: Exception => error(s"Cluster default configs could not be applied: $persistentProps", e)
case e: Exception => error(s"Cluster default configs could not be applied: ${persistentProps.keys()}", e)
}
}

Expand Down Expand Up @@ -469,7 +469,7 @@ class DynamicBrokerConfig(private val kafkaConfig: KafkaConfig) extends Logging
}
invalidProps.keys.foreach(props.remove)
val configSource = if (perBrokerConfig) "broker" else "default cluster"
error(s"Dynamic $configSource config contains invalid values: $invalidProps, these configs will be ignored", e)
error(s"Dynamic $configSource config contains invalid values in: ${invalidProps.keys}, these configs will be ignored", e)
}
}

Expand Down Expand Up @@ -555,7 +555,8 @@ class DynamicBrokerConfig(private val kafkaConfig: KafkaConfig) extends Logging
} catch {
case e: Exception =>
if (!validateOnly)
error(s"Failed to update broker configuration with configs : ${newConfig.originalsFromThisConfig}", e)
error(s"Failed to update broker configuration with configs : " +
s"${ConfigUtils.configMapToRedactedString(newConfig.originalsFromThisConfig, KafkaConfig.configDef)}", e)
throw new ConfigException("Invalid dynamic configuration", e)
}
}
Expand Down
11 changes: 11 additions & 0 deletions core/src/main/scala/kafka/server/KafkaApis.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,17 @@ class KafkaApis(val requestChannel: RequestChannel,
val metadataRequest = request.body[MetadataRequest]
val requestVersion = request.header.apiVersion

// Topic IDs are not supported for versions 10 and 11. Topic names can not be null in these versions.
if (!metadataRequest.isAllTopics) {
metadataRequest.data.topics.forEach{ topic =>
if (topic.name == null) {
throw new InvalidRequestException(s"Topic name can not be null for version ${metadataRequest.version}")
} else if (topic.topicId != Uuid.ZERO_UUID) {
throw new InvalidRequestException(s"Topic IDs are not supported in requests for version ${metadataRequest.version}")
}
}
}

val topics = if (metadataRequest.isAllTopics)
metadataCache.getAllTopics()
else
Expand Down
6 changes: 5 additions & 1 deletion core/src/main/scala/kafka/server/ZkAdminManager.scala
Original file line number Diff line number Diff line change
Expand Up @@ -415,8 +415,12 @@ class ZkAdminManager(val config: KafkaConfig,
info(message)
resource -> ApiError.fromThrowable(new InvalidRequestException(message, e))
case e: Throwable =>
val configProps = new Properties
config.entries.asScala.filter(_.value != null).foreach { configEntry =>
configProps.setProperty(configEntry.name, configEntry.value)
}
// Log client errors at a lower level than unexpected exceptions
val message = s"Error processing alter configs request for resource $resource, config $config"
val message = s"Error processing alter configs request for resource $resource, config ${toLoggableProps(resource, configProps).mkString(",")}"
if (e.isInstanceOf[ApiException])
info(message, e)
else
Expand Down
Loading