This is an automated email from the ASF dual-hosted git repository.

chia7712 pushed a commit to branch 3.9
in repository https://gitbox.apache.org/repos/asf/kafka.git


The following commit(s) were added to refs/heads/3.9 by this push:
     new afdde056717 KAFKA-19876 Replace the base image since openjdk image is 
deprecated (#20897)
afdde056717 is described below

commit afdde056717a6b658e7e251c34688856991a6336
Author: Ming-Yen Chung <[email protected]>
AuthorDate: Fri Nov 21 23:41:21 2025 +0800

    KAFKA-19876 Replace the base image since openjdk image is deprecated 
(#20897)
    
    The `openjdk` image is deprecated. I replaced it with
    `eclipse-temurin:8-jdk-jammy`. Trunk(#20886) uses `sapmachine`, but
    since `sapmachine` does not provide JDK 8 and older Kafka versions still
    require it. I switched to `eclipse-temurin` instead. Using `jammy` also
    requires upgrading the `ducktape` and `psutil` versions. So I also
    cherry-picked #17480, #17542, #17547 #17740, #19940, #20178 that upgrade
    dependencies and update their usage.
    
    I've tested a simple `pluggable_test` on my local mac with image
    `eclipse-temurin:8-jdk-jammy`
    
    ```
    ...
    docker build --memory=3200m --build-arg ducker_creator= --build-arg 
jdk_version=eclipse-temurin:8-jdk-jammy --build-arg UID=501 --build-arg 
KAFKA_MODE=jvm -t ducker-ak-eclipse-temurin-8-jdk-jammy -f 
/Users/ming/code/kafka/tests/docker/Dockerfile -- .
    
    ...
    
    
================================================================================
    SESSION REPORT (ALL TESTS)
    ducktape version: 0.12.0
    session_id:       2025-11-20--002
    run time:         26.096 seconds
    tests run:        2
    passed:           2
    flaky:            0
    failed:           0
    ignored:          0
    
================================================================================
    test_id:    
kafkatest.tests.client.pluggable_test.PluggableConsumerTest.test_start_stop.metadata_quorum=ISOLATED_KRAFT
    status:     PASS
    run time:   13.961 seconds
    
--------------------------------------------------------------------------------
    test_id:    
kafkatest.tests.client.pluggable_test.PluggableConsumerTest.test_start_stop.metadata_quorum=ZK
    status:     PASS
    run time:   11.888 seconds
    
--------------------------------------------------------------------------------
    ```
    
    Reviewers: PoAn Yang <[email protected]>, Chia-Ping Tsai
    <[email protected]>
---
 docker/common.py                                  |  5 ++---
 docker/docker_build_test.py                       |  3 +--
 docker/docker_official_image_build_test.py        | 10 +++++-----
 docker/prepare_docker_official_image_source.py    | 11 ++++-------
 tests/README.md                                   |  2 +-
 tests/docker/Dockerfile                           | 15 +++++++++------
 tests/docker/ducker-ak                            |  5 +++--
 tests/{unit/setup.cfg => docker/requirements.txt} | 18 ++++++++++--------
 tests/setup.cfg                                   |  2 +-
 tests/setup.py                                    | 12 ++++--------
 tests/unit/setup.cfg                              |  2 +-
 11 files changed, 41 insertions(+), 44 deletions(-)

diff --git a/docker/common.py b/docker/common.py
index aaab1eae1ff..a7cd3bfd203 100644
--- a/docker/common.py
+++ b/docker/common.py
@@ -18,7 +18,6 @@
 import subprocess
 import tempfile
 import os
-from distutils.dir_util import copy_tree
 import shutil
 
 def execute(command):
@@ -34,8 +33,8 @@ def get_input(message):
 def build_docker_image_runner(command, image_type):
     temp_dir_path = tempfile.mkdtemp()
     current_dir = os.path.dirname(os.path.realpath(__file__))
-    copy_tree(f"{current_dir}/{image_type}", f"{temp_dir_path}/{image_type}")
-    copy_tree(f"{current_dir}/resources", 
f"{temp_dir_path}/{image_type}/resources")
+    shutil.copytree(f"{current_dir}/{image_type}", 
f"{temp_dir_path}/{image_type}")
+    shutil.copytree(f"{current_dir}/resources", 
f"{temp_dir_path}/{image_type}/resources")
     command = command.replace("$DOCKER_FILE", 
f"{temp_dir_path}/{image_type}/Dockerfile")
     command = command.replace("$DOCKER_DIR", f"{temp_dir_path}/{image_type}")
     try:
diff --git a/docker/docker_build_test.py b/docker/docker_build_test.py
index 793148573f3..1c3499c865c 100755
--- a/docker/docker_build_test.py
+++ b/docker/docker_build_test.py
@@ -34,7 +34,6 @@ Usage:
 
 from datetime import date
 import argparse
-from distutils.dir_util import copy_tree
 import shutil
 from test.docker_sanity_test import run_tests
 from common import execute, build_docker_image_runner
@@ -49,7 +48,7 @@ def run_docker_tests(image, tag, kafka_url, image_type):
     temp_dir_path = tempfile.mkdtemp()
     try:
         current_dir = os.path.dirname(os.path.realpath(__file__))
-        copy_tree(f"{current_dir}/test/fixtures", f"{temp_dir_path}/fixtures")
+        shutil.copytree(f"{current_dir}/test/fixtures", 
f"{temp_dir_path}/fixtures", dirs_exist_ok=True)
         execute(["wget", "-nv", "-O", f"{temp_dir_path}/kafka.tgz", kafka_url])
         execute(["mkdir", f"{temp_dir_path}/fixtures/kafka"])
         execute(["tar", "xfz", f"{temp_dir_path}/kafka.tgz", "-C", 
f"{temp_dir_path}/fixtures/kafka", "--strip-components", "1"])
diff --git a/docker/docker_official_image_build_test.py 
b/docker/docker_official_image_build_test.py
index 3da68854c23..32869a1f4b2 100644
--- a/docker/docker_official_image_build_test.py
+++ b/docker/docker_official_image_build_test.py
@@ -34,7 +34,6 @@ Usage:
 """
 
 import argparse
-from distutils.dir_util import copy_tree
 import shutil
 from common import execute
 from docker_build_test import run_docker_tests
@@ -46,10 +45,11 @@ def build_docker_official_image(image, tag, kafka_version, 
image_type):
     image = f'{image}:{tag}'
     current_dir = os.path.dirname(os.path.realpath(__file__))
     temp_dir_path = tempfile.mkdtemp()
-    
copy_tree(f"{current_dir}/docker_official_images/{kafka_version}/{image_type}",
-              f"{temp_dir_path}/{image_type}")
-    
copy_tree(f"{current_dir}/docker_official_images/{kafka_version}/jvm/resources",
-              f"{temp_dir_path}/{image_type}/resources")
+    
shutil.copytree(f"{current_dir}/docker_official_images/{kafka_version}/{image_type}",
+              f"{temp_dir_path}/{image_type}", dirs_exist_ok=True)
+    
shutil.copytree(f"{current_dir}/docker_official_images/{kafka_version}/jvm/resources",
+              f"{temp_dir_path}/{image_type}/resources", dirs_exist_ok=True)
+    shutil.copy(f"{current_dir}/server.properties", 
f"{temp_dir_path}/{image_type}")
     command = f"docker build -f $DOCKER_FILE -t {image} $DOCKER_DIR"
     command = command.replace("$DOCKER_FILE", 
f"{temp_dir_path}/{image_type}/Dockerfile")
     command = command.replace("$DOCKER_DIR", f"{temp_dir_path}/{image_type}")
diff --git a/docker/prepare_docker_official_image_source.py 
b/docker/prepare_docker_official_image_source.py
index 25d57c53e0f..bbc539b5c4c 100644
--- a/docker/prepare_docker_official_image_source.py
+++ b/docker/prepare_docker_official_image_source.py
@@ -33,7 +33,6 @@ Usage:
 
 from datetime import date
 import argparse
-from distutils.dir_util import copy_tree
 import os
 import shutil
 import re
@@ -61,12 +60,10 @@ if __name__ == '__main__':
     args = parser.parse_args()
     kafka_url = 
f"https://archive.apache.org/dist/kafka/{args.kafka_version}/kafka_2.13-{args.kafka_version}.tgz";
     current_dir = os.path.dirname(os.path.realpath(__file__))
-    new_dir = os.path.join(
-        current_dir, f'docker_official_images', args.kafka_version)
+    new_dir = os.path.join(current_dir, 'docker_official_images', 
args.kafka_version)
     if os.path.exists(new_dir):
         shutil.rmtree(new_dir)
     os.makedirs(new_dir)
-    copy_tree(os.path.join(current_dir, args.image_type), 
os.path.join(new_dir, args.kafka_version, args.image_type))
-    copy_tree(os.path.join(current_dir, 'resources'), os.path.join(new_dir, 
args.kafka_version, args.image_type, 'resources'))
-    remove_args_and_hardcode_values(
-        os.path.join(new_dir, args.kafka_version, args.image_type, 
'Dockerfile'), args.kafka_version, kafka_url)
+    shutil.copytree(os.path.join(current_dir, args.image_type), 
os.path.join(new_dir, args.image_type), dirs_exist_ok=True)
+    shutil.copytree(os.path.join(current_dir, 'resources'), 
os.path.join(new_dir, args.image_type, 'resources'), dirs_exist_ok=True)
+    remove_args_and_hardcode_values(os.path.join(new_dir, args.image_type, 
'Dockerfile'), args.kafka_version, kafka_url)
diff --git a/tests/README.md b/tests/README.md
index e81aaecb140..b9b310a4e54 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -157,7 +157,7 @@ 
https://cwiki.apache.org/confluence/display/KAFKA/tutorial+-+set+up+and+run+Kafk
         $ cd kafka/tests
         $ virtualenv -p python3 venv
         $ . ./venv/bin/activate
-        $ python3 setup.py develop
+        $ python3 -m pip install --editable .
         $ cd ..  # back to base kafka directory
 
 * Run the bootstrap script to set up Vagrant for testing
diff --git a/tests/docker/Dockerfile b/tests/docker/Dockerfile
index be5a2363572..498cf62b242 100644
--- a/tests/docker/Dockerfile
+++ b/tests/docker/Dockerfile
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-ARG jdk_version=openjdk:8
+ARG jdk_version
 FROM $jdk_version AS build-native-image
 
 WORKDIR /build
@@ -60,11 +60,11 @@ ARG ducker_creator=default
 LABEL ducker.creator=$ducker_creator
 
 # Update Linux and install necessary utilities.
-# we have to install git since it is included in openjdk:8 but not openjdk:11
 RUN apt update && apt install -y sudo git netcat iptables rsync unzip wget 
curl jq coreutils openssh-server net-tools vim python3-pip python3-dev 
libffi-dev libssl-dev cmake pkg-config libfuse-dev iperf traceroute iproute2 
iputils-ping && apt-get -y clean
 RUN python3 -m pip install -U pip==21.1.1;
-# NOTE: ducktape 0.11.4 requires python3.9
-RUN pip3 install --upgrade cffi virtualenv pyasn1 boto3 pycrypto pywinrm 
ipaddress enum34 debugpy && pip3 install --upgrade "ducktape==0.11.4"
+# NOTE: ducktape 0.12.0 supports py 3.9, 3.10, 3.11 and 3.12
+COPY requirements.txt requirements.txt
+RUN pip3 install --upgrade -r requirements.txt
 
 COPY --from=build-native-image /build/kafka-binary/ /opt/kafka-binary/
 # Set up ssh
@@ -147,10 +147,13 @@ RUN useradd -u $UID -ms /bin/bash ducker \
   && mkdir -p /home/ducker/ \
   && rsync -aiq /root/.ssh/ /home/ducker/.ssh \
   && chown -R ducker /home/ducker/ /mnt/ /var/log/ \
-  && echo "PATH=$(runuser -l ducker -c 'echo $PATH'):$JAVA_HOME/bin" >> 
/home/ducker/.ssh/environment \
-  && echo 'PATH=$PATH:'"$JAVA_HOME/bin" >> /home/ducker/.profile \
   && echo 'ducker ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
 
+# Symlink all JDK binaries (java, jcmd, jps, etc.) to /usr/bin.
+# We don't add PATH env to ~/.ssh/environment because on some Linux 
distributions,
+# PAM (Pluggable Authentication Modules) may re-read /etc/environment, 
overwriting PATH.
+RUN cp -sn $JAVA_HOME/bin/* /usr/bin/
+
 USER ducker
 
 CMD sudo service ssh start && tail -f /dev/null
diff --git a/tests/docker/ducker-ak b/tests/docker/ducker-ak
index 21f0f642194..dcff0bda912 100755
--- a/tests/docker/ducker-ak
+++ b/tests/docker/ducker-ak
@@ -44,8 +44,9 @@ docker_run_memory_limit="2000m"
 # The default number of cluster nodes to bring up if a number is not specified.
 default_num_nodes=14
 
-# The default OpenJDK base image.
-default_jdk="openjdk:8"
+# The default JDK base image with apt-get support.
+# The openjdk image has been officially deprecated. For more information, see: 
https://hub.docker.com/_/openjdk
+default_jdk="eclipse-temurin:8-jdk-jammy"
 
 # The default ducker-ak image name.
 default_image_name="ducker-ak"
diff --git a/tests/unit/setup.cfg b/tests/docker/requirements.txt
similarity index 70%
copy from tests/unit/setup.cfg
copy to tests/docker/requirements.txt
index 3470da12185..91eaae441b4 100644
--- a/tests/unit/setup.cfg
+++ b/tests/docker/requirements.txt
@@ -13,11 +13,13 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# pytest configuration (can also be defined in tox.ini or pytest.ini file)
-#
-# To ease possible confusion, prefix muckrake *unit* tests with 'check' 
instead of 'test', since
-# many muckrake files, classes, and methods have 'test' somewhere in the name
-[pytest]
-python_files=check_*.py
-python_classes=Check
-python_functions=check_*
+cffi 
+virtualenv 
+pyasn1 
+boto3 
+pycrypto 
+pywinrm 
+ipaddress 
+debugpy 
+psutil
+ducktape==0.12.0
\ No newline at end of file
diff --git a/tests/setup.cfg b/tests/setup.cfg
index 974d5bb9a97..61e54430368 100644
--- a/tests/setup.cfg
+++ b/tests/setup.cfg
@@ -20,7 +20,7 @@
 #
 # To ease possible confusion, 'check' instead of 'test' as a prefix for unit 
tests, since
 # many system test files, classes, and methods have 'test' somewhere in the 
name
-[pytest]
+[tool:pytest]
 testpaths=unit
 python_files=check_*.py
 python_classes=Check
diff --git a/tests/setup.py b/tests/setup.py
index fd3b9738676..17476849d52 100644
--- a/tests/setup.py
+++ b/tests/setup.py
@@ -15,27 +15,24 @@
 
 import re
 import sys
-from setuptools import find_packages, setup
-from setuptools.command.test import test as TestCommand
+from setuptools import find_packages, setup, Command
 
 version = ''
 with open('kafkatest/__init__.py', 'r') as fd:
     version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), 
re.MULTILINE).group(1)
 
 
-class PyTest(TestCommand):
+class PyTest(Command):
     user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")]
 
     def initialize_options(self):
-        TestCommand.initialize_options(self)
         self.pytest_args = []
 
     def finalize_options(self):
-        TestCommand.finalize_options(self)
         self.test_args = []
         self.test_suite = True
 
-    def run_tests(self):
+    def run(self):
         # import here, cause outside the eggs aren't loaded
         import pytest
         print(self.pytest_args)
@@ -51,8 +48,7 @@ setup(name="kafkatest",
       license="apache2.0",
       packages=find_packages(),
       include_package_data=True,
-      install_requires=["ducktape==0.11.4", "requests==2.31.0"],
-      tests_require=["pytest", "mock"],
+      install_requires=["ducktape==0.12.0", "requests==2.32.4", 
"psutil==5.7.2", "pytest==8.3.3", "mock==5.1.0"],
       cmdclass={'test': PyTest},
       zip_safe=False
       )
diff --git a/tests/unit/setup.cfg b/tests/unit/setup.cfg
index 3470da12185..8de3c3de792 100644
--- a/tests/unit/setup.cfg
+++ b/tests/unit/setup.cfg
@@ -17,7 +17,7 @@
 #
 # To ease possible confusion, prefix muckrake *unit* tests with 'check' 
instead of 'test', since
 # many muckrake files, classes, and methods have 'test' somewhere in the name
-[pytest]
+[tool:pytest]
 python_files=check_*.py
 python_classes=Check
 python_functions=check_*

Reply via email to