KAFKA-13273: Add support for Java 17 (#11296)

Java 17 is at release candidate stage and it will be a LTS release once
it's out (previous LTS release was Java 11).

Details:
* Replace Java 16 with Java 17 in Jenkins and Readme.
* Replace `--illegal-access=permit` (which was removed from Java 17)
   with  `--add-opens` for the packages we require internal access to.
   Filed KAFKA-13275 for updating the tests not to require `--add-opens`
   (where possible).
* Update `release.py` to use JDK8. and JDK 17 (instead of JDK 8 and JDK 15).
* Removed all but one Streams test from `testsToExclude`. The
   Connect test exclusion list remains the same.
* Add notable change to upgrade.html
* Upgrade to Gradle 7.2 as it's required for proper Java 17 support.
* Upgrade mockito to 3.12.4 for better Java 17 support.
* Adjusted `KafkaRaftClientTest` and `QuorumStateTest` not to require
   private access to `jdk.internal.util.random`.

Reviewers: Manikumar Reddy <manikumar.reddy@gmail.com>, Chia-Ping Tsai <chia7712@gmail.com>
This commit is contained in:
Ismael Juma 2021-09-06 08:55:52 -07:00 committed by GitHub
parent 81667e2bf5
commit 0118330103
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 125 additions and 79 deletions

12
Jenkinsfile vendored
View File

@ -142,10 +142,10 @@ pipeline {
}
}
stage('JDK 16 and Scala 2.13') {
stage('JDK 17 and Scala 2.13') {
agent { label 'ubuntu' }
tools {
jdk 'jdk_16_latest'
jdk 'jdk_17_latest'
}
options {
timeout(time: 8, unit: 'HOURS')
@ -157,7 +157,7 @@ pipeline {
steps {
doValidation()
doTest(env)
echo 'Skipping Kafka Streams archetype test for Java 16'
echo 'Skipping Kafka Streams archetype test for Java 17'
}
}
@ -231,14 +231,14 @@ pipeline {
}
}
stage('JDK 16 and Scala 2.12') {
stage('JDK 17 and Scala 2.12') {
when {
not { changeRequest() }
beforeAgent true
}
agent { label 'ubuntu' }
tools {
jdk 'jdk_16_latest'
jdk 'jdk_17_latest'
}
options {
timeout(time: 8, unit: 'HOURS')
@ -250,7 +250,7 @@ pipeline {
steps {
doValidation()
doTest(env)
echo 'Skipping Kafka Streams archetype test for Java 16'
echo 'Skipping Kafka Streams archetype test for Java 17'
}
}
}

View File

@ -4,7 +4,7 @@ See our [web site](https://kafka.apache.org) for details on the project.
You need to have [Java](http://www.oracle.com/technetwork/java/javase/downloads/index.html) installed.
We build and test Apache Kafka with Java 8, 11 and 16. We set the `release` parameter in javac and scalac
We build and test Apache Kafka with Java 8, 11 and 17. We set the `release` parameter in javac and scalac
to `8` to ensure the generated binaries are compatible with Java 8 or higher (independently of the Java version
used for compilation). Java 8 support has been deprecated since Apache Kafka 3.0 and will be removed in Apache
Kafka 4.0 (see [KIP-750](https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=181308223) for more details).

View File

@ -102,8 +102,22 @@ ext {
defaultMaxHeapSize = "2g"
defaultJvmArgs = ["-Xss4m", "-XX:+UseParallelGC"]
if (JavaVersion.current() == JavaVersion.VERSION_16)
defaultJvmArgs.add("--illegal-access=permit")
// "JEP 403: Strongly Encapsulate JDK Internals" causes some tests to fail when they try
// to access internals (often via mocking libraries). We use `--add-opens` as a workaround
// for now and we'll fix it properly (where possible) via KAFKA-13275.
if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_16))
defaultJvmArgs.addAll(
"--add-opens=java.base/java.io=ALL-UNNAMED",
"--add-opens=java.base/java.nio=ALL-UNNAMED",
"--add-opens=java.base/java.nio.file=ALL-UNNAMED",
"--add-opens=java.base/java.util.concurrent=ALL-UNNAMED",
"--add-opens=java.base/java.util.regex=ALL-UNNAMED",
"--add-opens=java.base/java.util.stream=ALL-UNNAMED",
"--add-opens=java.base/java.text=ALL-UNNAMED",
"--add-opens=java.base/java.time=ALL-UNNAMED",
"--add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED"
)
userMaxForks = project.hasProperty('maxParallelForks') ? maxParallelForks.toInteger() : null
userIgnoreFailures = project.hasProperty('ignoreFailures') ? ignoreFailures : false
@ -359,7 +373,7 @@ 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
// Exclude PowerMock tests when running with Java 16 or newer until a version of PowerMock that supports the relevant versions 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([
@ -372,8 +386,7 @@ subprojects {
"**/WorkerSinkTaskTest.*", "**/WorkerSinkTaskThreadedTest.*", "**/WorkerSourceTaskTest.*",
"**/WorkerTaskTest.*", "**/WorkerTest.*", "**/RestServerTest.*",
// streams tests
"**/KafkaStreamsTest.*", "**/RepartitionTopicsTest.*", "**/RocksDBMetricsRecorderTest.*",
"**/StreamsMetricsImplTest.*", "**/StateManagerUtilTest.*", "**/TableSourceNodeTest.*"
"**/KafkaStreamsTest.*"
])
}

View File

@ -19,7 +19,12 @@
<script id="upgrade-template" type="text/x-handlebars-template">
<h5><a id="upgrade_300_notable" href="#upgrade_300_notable">Notable changes in 3.0.0</a></h5>
<h5><a id="upgrade_310_notable" href="#upgrade_310_notable">Notable changes in 3.1.0</a></h5>
<ul>
<li>Apache Kafka supports Java 17.</li>
</ul>
<h5><a id="upgrade_310_notable" href="#upgrade_300_notable">Notable changes in 3.0.0</a></h5>
<ul>
<li>ZooKeeper has been upgraded to version 3.6.3.</li>
<li>A preview of KRaft mode is available, though upgrading to it from the 2.8 Early Access release is not possible. See

View File

@ -62,7 +62,7 @@ versions += [
checkstyle: "8.36.2",
commonsCli: "1.4",
dropwizardMetrics: "4.1.12.1",
gradle: "7.1.1",
gradle: "7.2",
grgit: "4.1.0",
httpclient: "4.5.13",
easymock: "4.3",
@ -100,7 +100,7 @@ versions += [
lz4: "1.7.1",
mavenArtifact: "3.8.1",
metrics: "2.2.0",
mockito: "3.9.0",
mockito: "3.12.4",
netty: "4.1.62.Final",
powermock: "2.0.9",
reflections: "0.9.12",

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=9bb8bc05f562f2d42bdf1ba8db62f6b6fa1c3bf6c392228802cc7cb0578fe7e0
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-all.zip
distributionSha256Sum=a8da5b02437a60819cad23e10fc7e9cf32bcb57029d9cb277e26eeff76ce014b
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

4
gradlew vendored
View File

@ -72,7 +72,7 @@ case "`uname`" in
Darwin* )
darwin=true
;;
MINGW* )
MSYS* | MINGW* )
msys=true
;;
NONSTOP* )
@ -84,7 +84,7 @@ esac
# Loop in case we encounter an error.
for attempt in 1 2 3; do
if [ ! -e "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" ]; then
if ! curl -s -S --retry 3 -L -o "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" "https://raw.githubusercontent.com/gradle/gradle/v7.1.1/gradle/wrapper/gradle-wrapper.jar"; then
if ! curl -s -S --retry 3 -L -o "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" "https://raw.githubusercontent.com/gradle/gradle/v7.2.0/gradle/wrapper/gradle-wrapper.jar"; then
rm -f "$APP_HOME/gradle/wrapper/gradle-wrapper.jar"
# Pause for a bit before looping in case the server throttled us.
sleep 5

View File

@ -101,9 +101,7 @@ public class KafkaRaftClientTest {
int epoch = 2;
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
.updateRandom(random -> {
Mockito.doReturn(0).when(random).nextInt(DEFAULT_ELECTION_TIMEOUT_MS);
})
.updateRandom(r -> r.mockNextInt(DEFAULT_ELECTION_TIMEOUT_MS, 0))
.withElectedLeader(epoch, localId)
.build();
@ -126,9 +124,7 @@ public class KafkaRaftClientTest {
int epoch = 2;
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
.updateRandom(random -> {
Mockito.doReturn(0).when(random).nextInt(DEFAULT_ELECTION_TIMEOUT_MS);
})
.updateRandom(r -> r.mockNextInt(DEFAULT_ELECTION_TIMEOUT_MS, 0))
.withVotedCandidate(epoch, localId)
.build();
@ -151,11 +147,9 @@ public class KafkaRaftClientTest {
int epoch = 2;
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
.updateRandom(random -> {
Mockito.doReturn(0).when(random).nextInt(DEFAULT_ELECTION_TIMEOUT_MS);
})
.withElectedLeader(epoch, localId)
.build();
.updateRandom(r -> r.mockNextInt(DEFAULT_ELECTION_TIMEOUT_MS, 0))
.withElectedLeader(epoch, localId)
.build();
// Resign from leader, will restart in resigned state
assertTrue(context.client.quorum().isResigned());
@ -181,11 +175,9 @@ public class KafkaRaftClientTest {
int epoch = 2;
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
.updateRandom(random -> {
Mockito.doReturn(0).when(random).nextInt(DEFAULT_ELECTION_TIMEOUT_MS);
})
.withVotedCandidate(epoch, localId)
.build();
.updateRandom(r -> r.mockNextInt(DEFAULT_ELECTION_TIMEOUT_MS, 0))
.withVotedCandidate(epoch, localId)
.build();
// Resign from candidate, will restart in candidate state
assertTrue(context.client.quorum().isCandidate());
@ -235,11 +227,9 @@ public class KafkaRaftClientTest {
int epoch = 2;
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
.updateRandom(random -> {
Mockito.doReturn(0).when(random).nextInt(DEFAULT_ELECTION_TIMEOUT_MS);
})
.withElectedLeader(epoch, localId)
.build();
.updateRandom(r -> r.mockNextInt(DEFAULT_ELECTION_TIMEOUT_MS, 0))
.withElectedLeader(epoch, localId)
.build();
// Resign from leader, will restart in resigned state
assertTrue(context.client.quorum().isResigned());
@ -262,9 +252,7 @@ public class KafkaRaftClientTest {
int epoch = 2;
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
.updateRandom(random -> {
Mockito.doReturn(0).when(random).nextInt(DEFAULT_ELECTION_TIMEOUT_MS);
})
.updateRandom(r -> r.mockNextInt(DEFAULT_ELECTION_TIMEOUT_MS, 0))
.withElectedLeader(epoch, localId)
.build();
@ -728,9 +716,7 @@ public class KafkaRaftClientTest {
Set<Integer> voters = Utils.mkSet(localId, otherNodeId);
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
.updateRandom(random -> {
Mockito.doReturn(jitterMs).when(random).nextInt(Mockito.anyInt());
})
.updateRandom(r -> r.mockNextInt(jitterMs))
.withUnknownLeader(epoch - 1)
.build();
@ -1238,9 +1224,7 @@ public class KafkaRaftClientTest {
Set<Integer> voters = Utils.mkSet(localId, otherNodeId);
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
.updateRandom(random -> {
Mockito.doReturn(exponentialFactor).when(random).nextInt(Mockito.anyInt());
})
.updateRandom(r -> r.mockNextInt(exponentialFactor))
.build();
context.assertUnknownLeader(0);
@ -2184,9 +2168,7 @@ public class KafkaRaftClientTest {
Set<Integer> voters = Utils.mkSet(localId, otherNodeId);
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
.updateRandom(random -> {
Mockito.doReturn(0).when(random).nextInt(DEFAULT_ELECTION_TIMEOUT_MS);
})
.updateRandom(r -> r.mockNextInt(DEFAULT_ELECTION_TIMEOUT_MS, 0))
.withUnknownLeader(epoch - 1)
.build();
@ -2395,9 +2377,7 @@ public class KafkaRaftClientTest {
Set<Integer> voters = Utils.mkSet(localId, otherNodeId);
RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters)
.updateRandom(random -> {
Mockito.doReturn(0).when(random).nextInt(DEFAULT_ELECTION_TIMEOUT_MS);
})
.updateRandom(r -> r.mockNextInt(DEFAULT_ELECTION_TIMEOUT_MS, 0))
.withUnknownLeader(epoch - 1)
.build();
@ -2799,4 +2779,5 @@ public class KafkaRaftClientTest {
private static KafkaMetric getMetric(final Metrics metrics, final String name) {
return metrics.metrics().get(metrics.metricName(name, "raft-metrics"));
}
}

View File

@ -0,0 +1,51 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.kafka.raft;
import java.util.OptionalInt;
import java.util.Random;
import java.util.function.IntFunction;
/**
* A Random instance that makes it easy to modify the behavior of certain methods for test purposes.
*/
class MockableRandom extends Random {
private IntFunction<OptionalInt> nextIntFunction = __ -> OptionalInt.empty();
public MockableRandom(long seed) {
super(seed);
}
public void mockNextInt(int expectedBound, int returnValue) {
this.nextIntFunction = b -> {
if (b == expectedBound)
return OptionalInt.of(returnValue);
else
return OptionalInt.empty();
};
}
public void mockNextInt(int returnValue) {
this.nextIntFunction = __ -> OptionalInt.of(returnValue);
}
@Override
public int nextInt(int bound) {
return nextIntFunction.apply(bound).orElse(super.nextInt(bound));
}
}

View File

@ -29,7 +29,6 @@ import java.util.Collections;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Random;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -45,7 +44,7 @@ public class QuorumStateTest {
private final MockTime time = new MockTime();
private final int electionTimeoutMs = 5000;
private final int fetchTimeoutMs = 10000;
private final Random random = Mockito.spy(new Random(1));
private final MockableRandom random = new MockableRandom(1L);
private BatchAccumulator<?> accumulator = Mockito.mock(BatchAccumulator.class);
@ -92,7 +91,7 @@ public class QuorumStateTest {
store.writeElectionState(ElectionState.withUnknownLeader(epoch, voters));
int jitterMs = 2500;
Mockito.doReturn(jitterMs).when(random).nextInt(Mockito.anyInt());
random.mockNextInt(jitterMs);
QuorumState state = buildQuorumState(voters);
state.initialize(new OffsetAndEpoch(0L, 0));
@ -132,7 +131,7 @@ public class QuorumStateTest {
store.writeElectionState(ElectionState.withVotedCandidate(epoch, node1, voters));
int jitterMs = 2500;
Mockito.doReturn(jitterMs).when(random).nextInt(Mockito.anyInt());
random.mockNextInt(jitterMs);
QuorumState state = buildQuorumState(voters);
state.initialize(new OffsetAndEpoch(0L, logEndEpoch));
@ -156,7 +155,7 @@ public class QuorumStateTest {
store.writeElectionState(election);
int jitterMs = 2500;
Mockito.doReturn(jitterMs).when(random).nextInt(Mockito.anyInt());
random.mockNextInt(jitterMs);
QuorumState state = buildQuorumState(voters);
state.initialize(new OffsetAndEpoch(0L, logEndEpoch));
@ -188,7 +187,7 @@ public class QuorumStateTest {
// The election timeout should be reset after we become a candidate again
int jitterMs = 2500;
Mockito.doReturn(jitterMs).when(random).nextInt(Mockito.anyInt());
random.mockNextInt(jitterMs);
QuorumState state = buildQuorumState(voters);
state.initialize(new OffsetAndEpoch(0L, logEndEpoch));
@ -234,7 +233,7 @@ public class QuorumStateTest {
// The election timeout should be reset after we become a candidate again
int jitterMs = 2500;
Mockito.doReturn(jitterMs).when(random).nextInt(Mockito.anyInt());
random.mockNextInt(jitterMs);
state.transitionToCandidate();
assertTrue(state.isCandidate());
@ -509,7 +508,7 @@ public class QuorumStateTest {
state.transitionToUnattached(5);
int jitterMs = 2500;
Mockito.doReturn(jitterMs).when(random).nextInt(electionTimeoutMs);
random.mockNextInt(electionTimeoutMs, jitterMs);
state.transitionToVoted(5, otherNodeId);
VotedState votedState = state.votedStateOrThrow();
@ -546,7 +545,7 @@ public class QuorumStateTest {
state.transitionToUnattached(5);
int jitterMs = 2500;
Mockito.doReturn(jitterMs).when(random).nextInt(electionTimeoutMs);
random.mockNextInt(electionTimeoutMs, jitterMs);
state.transitionToCandidate();
assertTrue(state.isCandidate());
@ -644,7 +643,7 @@ public class QuorumStateTest {
state.transitionToVoted(5, node1);
int jitterMs = 2500;
Mockito.doReturn(jitterMs).when(random).nextInt(electionTimeoutMs);
random.mockNextInt(electionTimeoutMs, jitterMs);
state.transitionToCandidate();
assertTrue(state.isCandidate());
CandidateState candidateState = state.candidateStateOrThrow();
@ -798,7 +797,7 @@ public class QuorumStateTest {
state.transitionToFollower(8, node2);
int jitterMs = 2500;
Mockito.doReturn(jitterMs).when(random).nextInt(electionTimeoutMs);
random.mockNextInt(electionTimeoutMs, jitterMs);
state.transitionToCandidate();
assertTrue(state.isCandidate());
CandidateState candidateState = state.candidateStateOrThrow();
@ -828,7 +827,7 @@ public class QuorumStateTest {
state.transitionToFollower(8, node2);
int jitterMs = 2500;
Mockito.doReturn(jitterMs).when(random).nextInt(electionTimeoutMs);
random.mockNextInt(electionTimeoutMs, jitterMs);
state.transitionToUnattached(9);
assertTrue(state.isUnattached());
UnattachedState unattachedState = state.unattachedStateOrThrow();
@ -861,7 +860,7 @@ public class QuorumStateTest {
state.transitionToFollower(8, node2);
int jitterMs = 2500;
Mockito.doReturn(jitterMs).when(random).nextInt(electionTimeoutMs);
random.mockNextInt(electionTimeoutMs, jitterMs);
state.transitionToVoted(9, node1);
assertTrue(state.isVoted());
VotedState votedState = state.votedStateOrThrow();
@ -1071,5 +1070,4 @@ public class QuorumStateTest {
state.initialize(new OffsetAndEpoch(0L, logEndEpoch));
return state;
}
}

View File

@ -16,6 +16,7 @@
*/
package org.apache.kafka.raft;
import java.util.function.Consumer;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.Uuid;
import org.apache.kafka.common.memory.MemoryPool;
@ -60,7 +61,6 @@ import org.apache.kafka.snapshot.RawSnapshotWriter;
import org.apache.kafka.snapshot.SnapshotReader;
import org.apache.kafka.test.TestCondition;
import org.apache.kafka.test.TestUtils;
import org.mockito.Mockito;
import java.io.IOException;
import java.net.InetSocketAddress;
@ -75,9 +75,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Random;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import static org.apache.kafka.raft.RaftUtil.hasValidTopicPartition;
@ -128,7 +126,7 @@ public final class RaftClientTestContext {
private final MockMessageQueue messageQueue = new MockMessageQueue();
private final MockTime time = new MockTime();
private final QuorumStateStore quorumStateStore = new MockQuorumStateStore();
private final Random random = Mockito.spy(new Random(1));
private final MockableRandom random = new MockableRandom(1L);
private final LogContext logContext = new LogContext();
private final MockLog log = new MockLog(METADATA_PARTITION, Uuid.METADATA_TOPIC_ID, logContext);
private final Set<Integer> voters;
@ -164,7 +162,7 @@ public final class RaftClientTestContext {
return this;
}
Builder updateRandom(Consumer<Random> consumer) {
Builder updateRandom(Consumer<MockableRandom> consumer) {
consumer.accept(random);
return this;
}

View File

@ -256,7 +256,7 @@ def command_stage_docs():
sys.exit("%s doesn't exist or does not appear to be the kafka-site repository" % kafka_site_repo_path)
prefs = load_prefs()
jdk15_env = get_jdk(prefs, 15)
jdk17_env = get_jdk(prefs, 17)
save_prefs(prefs)
version = get_version()
@ -265,7 +265,7 @@ def command_stage_docs():
# version due to already having bumped the bugfix version number.
gradle_version_override = docs_release_version(version)
cmd("Building docs", "./gradlew -Pversion=%s clean siteDocsTar aggregatedJavadoc" % gradle_version_override, cwd=REPO_HOME, env=jdk15_env)
cmd("Building docs", "./gradlew -Pversion=%s clean siteDocsTar aggregatedJavadoc" % gradle_version_override, cwd=REPO_HOME, env=jdk17_env)
docs_tar = os.path.join(REPO_HOME, 'core', 'build', 'distributions', 'kafka_2.13-%s-site-docs.tgz' % gradle_version_override)
@ -426,7 +426,7 @@ prefs = load_prefs()
if not user_ok("""Requirements:
1. Updated docs to reference the new release version where appropriate.
2. JDK8 and JDK15 compilers and libraries
2. JDK8 and JDK17 compilers and libraries
3. Your Apache ID, already configured with SSH keys on id.apache.org and SSH keys available in this shell session
4. All issues in the target release resolved with valid resolutions (if not, this script will report the problematic JIRAs)
5. A GPG key used for signing the release. This key should have been added to public Apache servers and the KEYS file on the Kafka site
@ -511,7 +511,7 @@ if not rc:
apache_id = get_pref(prefs, 'apache_id', lambda: raw_input("Enter your apache username: "))
jdk8_env = get_jdk(prefs, 8)
jdk15_env = get_jdk(prefs, 15)
jdk17_env = get_jdk(prefs, 17)
def select_gpg_key():
print("Here are the available GPG keys:")
@ -600,7 +600,7 @@ cmd("Creating source archive", "git archive --format tar.gz --prefix kafka-%(rel
cmd("Building artifacts", "./gradlew clean && ./gradlewAll releaseTarGz", cwd=kafka_dir, env=jdk8_env, shell=True)
cmd("Copying artifacts", "cp %s/core/build/distributions/* %s" % (kafka_dir, artifacts_dir), shell=True)
cmd("Building docs", "./gradlew clean aggregatedJavadoc", cwd=kafka_dir, env=jdk15_env)
cmd("Building docs", "./gradlew clean aggregatedJavadoc", cwd=kafka_dir, env=jdk17_env)
cmd("Copying docs", "cp -R %s/build/docs/javadoc %s" % (kafka_dir, artifacts_dir))
for filename in os.listdir(artifacts_dir):