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

tuhaihe pushed a commit to branch REL_2_STABLE
in repository https://gitbox.apache.org/repos/asf/cloudberry-pxf.git

commit 739a50fda3cfc5a04c5e4460cafc5cc20e4329bb
Author: Dianjin Wang <[email protected]>
AuthorDate: Wed Sep 2 14:50:01 2026 +0800

    CI: add Rocky 10 support (#136)
    
    * CI: add Rocky 10 support
    
    Mirror the existing Rocky 9 lane for Rocky 10 so the two distros run the
    same jobs with the same test scope:
    
    - build-cloudberry-rpm-rocky10 builds the Cloudberry RPM on the Rocky 10
      build image and publishes it as its own artifact/cache.
    - build-docker-images-rocky10 builds the singlecluster image.
    - pxf-test-rocky10 runs the same 16 test groups as pxf-test-rocky9.
    - rocky10 is added to the testcontainer image matrix and to the
      testcontainer test matrix.
    - test-summary gains a Rocky 10 section.
    
    The Rocky 10 jobs are derived from the Rocky 9 ones by substituting the
    distro identifiers only, so the two matrices cannot drift apart.
    
    RHEL/Rocky 10 dropped OpenJDK 8 and 11 from its repositories (only 21 and
    25 remain), but Hadoop 3.1.2 still needs JDK 8 and the PXF server builds
    with JDK 11. Both images therefore pull Temurin 8/11 from the Adoptium
    repository on 10+ and expose them under the /usr/lib/jvm/java-1.8.0-openjdk
    and /usr/lib/jvm/java-11-openjdk names the CI scripts already resolve
    JAVA_HADOOP and JAVA_BUILD to, so no script needs a Rocky 10 special case.
    The Adoptium package layout has changed names across releases, so the JDK
    home is discovered with rpm -ql rather than hardcoded, and the resulting
    symlinks are verified at image build time. Older releases keep using the
    distro OpenJDK packages unchanged.
    
    For the same reason the shared entrypoint no longer asks dnf for the
    OpenJDK 8/11 packages on 10+ -- the image already provides them.
    
    * CI: disable rpath QA when building the Rocky 10 Cloudberry RPM
    
    RHEL/Rocky 10 runs check-rpaths as part of the default %install
    post-processing chain, which RHEL 9 never invokes. Cloudberry links its
    libraries with a RUNPATH into $INSTALL_PREFIX/lib on purpose, and the
    plpython3 modules additionally carry /usr/lib64 and /ext/python/lib, so
    the check aborts the build:
    
      ERROR 0002: file '.../lib/postgresql/hstore.so' contains an invalid
                  runpath '/usr/local/cloudberry-db/lib'
      ERROR 0001: file '.../lib/postgresql/plpython3.so' contains a standard
                  runpath '/usr/lib64'
      error: Bad exit status from /var/tmp/rpm-tmp.X7bvEp (%install)
    
    This RPM only ever installs Cloudberry into a CI container and is never
    published, so set QA_RPATHS to switch the rpath QA off entirely, which
    leaves the Rocky 10 lane behaving like the Rocky 9 one.
    
    Verified against the apache/incubator-cloudberry:cbdb-build-rocky10-latest
    image with a REL_2_STABLE checkout: the build script fails at check-rpaths
    without QA_RPATHS and produces apache-cloudberry-db-99.0.0-1.el10.x86_64.rpm
    with it, check-rpaths still running but reporting no errors. The same probe
    on the Rocky 9 image shows check-rpaths is never invoked there.
    
    * CI: stop naming ssh-dss in the sshd config on Rocky 10
    
    OpenSSH 9.8 removed DSA support entirely and RHEL/Rocky 10 ships 9.9, so
    the key types the automation sshd config asks for no longer parse there:
    
      /etc/ssh/sshd_config.d/pxf-automation.conf line 2: Bad key types
      '+ssh-rsa,ssh-dss'.
    
    sshd rejects the whole file and exits, which took down every Rocky 10 job
    in both lanes -- the 16 pxf-test-rocky10 jobs failed in "Start Services",
    and the 6 rocky10 testcontainer jobs failed with "entrypoint.sh failed
    (exit 1)" out of PXFCloudberryContainer, since both entrypoints write the
    same file.
    
    Ask for ssh-dss only when the local sshd still knows it, so Ubuntu and
    Rocky 9 keep the exact configuration they have today while Rocky 10 gets
    ssh-rsa alone. ssh-keygen -A does not generate DSA host keys on either
    release, so nothing is lost. The KEX line is unchanged: Rocky 10 still
    offers the group1-sha1 / group-exchange-sha1 / group14-sha1 algorithms
    the Ganymed SSH-2 client needs.
    
    Also validate the configuration with sshd -t before starting and echo the
    log on failure. This failure produced nothing but "Failed to start sshd,
    check /tmp/sshd.log" in CI, with the log never printed and the parse error
    never captured.
    
    Verified on apache/incubator-cloudberry:cbdb-build-rocky10-latest: sshd -t
    rejects '+ssh-rsa,ssh-dss' and accepts '+ssh-rsa', and a full run of the
    shared entrypoint against that image now reports "sshd key types: ssh-rsa"
    followed by "sshd is running on port 22", then installs Cloudberry and
    brings up the demo cluster. The same probe on the Rocky 9 image shows
    ssh-dss is still supported and the config is unchanged there.
    
    * CI: make the automation sshd drop-in win over the crypto policy
    
    The automation framework talks SSH through Ganymed SSH-2 (ch.ethz.ssh2),
    which only knows the ssh-rsa / ssh-dss host key algorithms. On Rocky 10
    every Hadoop-dependent test group failed to reach the cluster:
    
      java.io.IOException: There was a problem while connecting to localhost:22
          at ch.ethz.ssh2.Connection.connect(Connection.java:805)
          at ...cluster.SingleCluster.init(SingleCluster.java:37)
    
    sshd was running and the KEX algorithms negotiated fine; the handshake
    died on the host key:
    
      debug1: kex: algorithm: diffie-hellman-group14-sha1
      debug1: kex: host key algorithm: (no match)
      Unable to negotiate: no matching host key type found.
        Their offer: rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp256,ssh-ed25519
    
    ssh-rsa was missing from the offer even though pxf-automation.conf asks
    for it, because sshd keeps the FIRST value it obtains for a keyword and
    the drop-ins are read in name order:
    
      40-redhat-crypto-policies.conf   -> Include .../opensshserver.config
      50-redhat.conf
      pxf-automation.conf              -> HostKeyAlgorithms +ssh-rsa (ignored)
    
    So the crypto policy always won and our directive never applied. Rocky 9
    did not notice because its image has no 40-redhat-crypto-policies.conf and
    its LEGACY policy still carries ssh-rsa; Rocky 10 removed SHA-1 from every
    policy, leaving nothing to enable it.
    
    Write the file as 01-pxf-automation.conf so it is read before the crypto
    policy include, and drop any file left under the old name.
    
    Verified on both build images by replaying setup_ssh and connecting with
    exactly the algorithm set Ganymed offers: ssh-rsa appears in sshd -T on
    both, and the connection succeeds on Rocky 9 (ssh-rsa,ssh-dss) as well as
    Rocky 10 (ssh-rsa). Confirmed in the live rocky10 container that the
    rename turns the failing handshake into a successful gpadmin login.
---
 .github/workflows/pxf-ci.yml                       | 295 ++++++++++++++++++++-
 .../testcontainers/PXFCloudberryContainer.java     |   1 +
 .../resources/testcontainers/pxf-cbdb/Dockerfile   |  22 +-
 .../testcontainers/pxf-cbdb/script/entrypoint.sh   |  32 ++-
 ci/docker/pxf-cbdb-dev/common/script/entrypoint.sh |  46 +++-
 ci/docker/pxf-cbdb-dev/rocky10/docker-compose.yml  |  58 ++++
 .../rocky10/script/build_cloudberry_rpm.sh         | 118 +++++++++
 ci/singlecluster/Dockerfile                        |  32 ++-
 8 files changed, 583 insertions(+), 21 deletions(-)

diff --git a/.github/workflows/pxf-ci.yml b/.github/workflows/pxf-ci.yml
index c1fa038b..a4b01db1 100644
--- a/.github/workflows/pxf-ci.yml
+++ b/.github/workflows/pxf-ci.yml
@@ -191,6 +191,79 @@ jobs:
         path: workspace/cloudberry-source-rocky9.tar.gz
         retention-days: 7
 
+  build-cloudberry-rpm-rocky10:
+    name: Build Cloudberry RPM Package (Rocky 10)
+    runs-on: ubuntu-latest
+    container:
+      image: apache/incubator-cloudberry:cbdb-build-rocky10-latest
+      options: --user root
+    steps:
+    - name: Get month number
+      id: get-month
+      run: echo "month=$(/bin/date -u '+%Y-%m')" >> "$GITHUB_OUTPUT"
+
+    - name: Restore RPM Rocky 10 cache
+      id: cache-rpm-rocky10
+      uses: actions/cache/restore@v4
+      with:
+        path: |
+          workspace/cloudberry-rpm
+          workspace/cloudberry-source-rocky10.tar.gz
+        key: cloudberry-rpm-rocky10-${{ runner.os }}-${{ 
steps.get-month.outputs.month }}
+
+    - name: Checkout Cloudberry source
+      if: steps.cache-rpm-rocky10.outputs.cache-hit != 'true'
+      uses: actions/checkout@v4
+      with:
+        repository: apache/cloudberry
+        ref: ${{ env.CLOUDBERRY_VERSION }}
+        path: workspace/cloudberry
+        submodules: true
+
+    - name: Checkout PXF source (for build script)
+      if: steps.cache-rpm-rocky10.outputs.cache-hit != 'true'
+      uses: actions/checkout@v4
+      with:
+        path: cloudberry-pxf
+
+    - name: Build Cloudberry RPM (Rocky 10)
+      if: steps.cache-rpm-rocky10.outputs.cache-hit != 'true'
+      run: |
+        export WORKSPACE=$PWD/workspace
+        export CLOUDBERRY_VERSION=99.0.0
+        export CLOUDBERRY_BUILD=1
+        bash 
cloudberry-pxf/ci/docker/pxf-cbdb-dev/rocky10/script/build_cloudberry_rpm.sh
+
+    - name: Package Cloudberry source
+      if: steps.cache-rpm-rocky10.outputs.cache-hit != 'true'
+      run: |
+        cd workspace
+        tar czf cloudberry-source-rocky10.tar.gz cloudberry/
+
+    - name: Save RPM Rocky 10 cache
+      # save cache from default branch only (this cache can be reused between 
PRs)
+      if: steps.cache-rpm-rocky10.outputs.cache-hit != 'true' && github.ref == 
'refs/heads/main'
+      uses: actions/cache/save@v4
+      with:
+        path: |
+          workspace/cloudberry-rpm
+          workspace/cloudberry-source-rocky10.tar.gz
+        key: cloudberry-rpm-rocky10-${{ runner.os }}-${{ 
steps.get-month.outputs.month }}
+
+    - name: Upload RPM artifact (Rocky 10)
+      uses: actions/upload-artifact@v4
+      with:
+        name: cloudberry-rpm-rocky10
+        path: workspace/cloudberry-rpm/*.rpm
+        retention-days: 7
+
+    - name: Upload Cloudberry source artifact (Rocky 10)
+      uses: actions/upload-artifact@v4
+      with:
+        name: cloudberry-source-rocky10
+        path: workspace/cloudberry-source-rocky10.tar.gz
+        retention-days: 7
+
   build-docker-images:
     name: Build Docker Images (Ubuntu)
     runs-on: ubuntu-latest
@@ -252,6 +325,28 @@ jobs:
         path: /tmp/singlecluster-rocky9-image.tar
         retention-days: 1
 
+  build-docker-images-rocky10:
+    name: Build Docker Images (Rocky 10)
+    runs-on: ubuntu-latest
+    steps:
+    - name: Checkout PXF source
+      uses: actions/checkout@v4
+      with:
+        path: cloudberry-pxf
+
+    - name: Build singlecluster Rocky 10 image
+      run: |
+        cd cloudberry-pxf/ci/singlecluster
+        docker build --build-arg 
BASE_IMAGE=apache/incubator-cloudberry:cbdb-build-rocky10-latest -t 
pxf/singlecluster-rocky10:3 .
+        docker save pxf/singlecluster-rocky10:3 > 
/tmp/singlecluster-rocky10-image.tar
+
+    - name: Upload singlecluster Rocky 10 image
+      uses: actions/upload-artifact@v4
+      with:
+        name: singlecluster-rocky10-image
+        path: /tmp/singlecluster-rocky10-image.tar
+        retention-days: 1
+
   build-pxf-cbdb-testcontainer-image:
     name: Build Testcontainer Images (${{ matrix.distro }})
     runs-on: ubuntu-latest
@@ -266,6 +361,9 @@ jobs:
           - distro: rocky9
             base_image: apache/incubator-cloudberry:cbdb-build-rocky9-latest
             image_tag: pxf/cbdb-testcontainer-rocky9:1
+          - distro: rocky10
+            base_image: apache/incubator-cloudberry:cbdb-build-rocky10-latest
+            image_tag: pxf/cbdb-testcontainer-rocky10:1
     steps:
       - name: Get month number
         id: get-month
@@ -688,6 +786,195 @@ jobs:
         fi
 
 
+  # Stage 2b2: Rocky 10 parallel test jobs
+  pxf-test-rocky10:
+    name: Test PXF Rocky10 - ${{ matrix.test_group }}
+    needs: [build-cloudberry-rpm-rocky10, build-docker-images-rocky10]
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        test_group:
+          - cli
+          - external-table
+          - fdw
+          - server
+          - smoke
+          - hdfs
+          - hcatalog
+          - hcfs
+          - hive
+          - hbase
+          - profile
+          - proxy
+          - unused
+          - features
+          - features_fdw
+          - load
+    steps:
+    - name: Free disk space
+      run: |
+        sudo rm -rf /usr/share/dotnet
+        sudo rm -rf /opt/ghc
+        sudo rm -rf /usr/local/share/boost
+        sudo rm -rf /usr/local/lib/android
+        sudo rm -rf /opt/hostedtoolcache
+        sudo docker system prune -af
+        df -h
+
+    - name: Checkout PXF source
+      uses: actions/checkout@v4
+      with:
+        fetch-depth: 1
+        path: cloudberry-pxf
+        submodules: true
+
+    - name: Download Cloudberry RPM (Rocky 10)
+      uses: actions/download-artifact@v4
+      with:
+        name: cloudberry-rpm-rocky10
+        path: /tmp
+
+    - name: Download Cloudberry source (Rocky 10)
+      uses: actions/download-artifact@v4
+      with:
+        name: cloudberry-source-rocky10
+        path: /tmp
+
+    - name: Download singlecluster Rocky 10 image
+      uses: actions/download-artifact@v4
+      with:
+        name: singlecluster-rocky10-image
+        path: /tmp
+
+    - name: Load singlecluster Rocky 10 image
+      run: |
+        docker load < /tmp/singlecluster-rocky10-image.tar
+
+    - name: Prepare Cloudberry source
+      run: |
+        tar xzf /tmp/cloudberry-source-rocky10.tar.gz
+        chmod -R u+rwX,go+rX cloudberry
+
+    - name: Restore Maven cache
+      id: cache-mvn
+      uses: actions/cache/restore@v4
+      with:
+        path: .m2
+        key: mvn-container-${{ hashFiles('cloudberry-pxf/automation/pom.xml') 
}}
+        restore-keys: |
+          mvn-container-
+
+    - name: Prepare Maven cache dir
+      run: |
+        mkdir -p .m2
+        chmod -R a+rwX .m2 || true
+
+    - name: Start Services
+      id: start_services
+      run: |
+        cd cloudberry-pxf
+        docker compose -f ci/docker/pxf-cbdb-dev/rocky10/docker-compose.yml 
down -v || true
+        docker compose -f ci/docker/pxf-cbdb-dev/rocky10/docker-compose.yml up 
-d
+        docker exec pxf-cbdb-dev sudo chown -R gpadmin:gpadmin 
/home/gpadmin/workspace/cloudberry
+        docker exec pxf-cbdb-dev sudo chown -R gpadmin:gpadmin 
/home/gpadmin/.m2
+        docker cp /tmp/*.rpm pxf-cbdb-dev:/tmp/
+        docker exec pxf-cbdb-dev sudo chown gpadmin:gpadmin /tmp/*.rpm
+        docker exec pxf-cbdb-dev bash -lc "cd 
/home/gpadmin/workspace/cloudberry-pxf && 
./ci/docker/pxf-cbdb-dev/common/script/entrypoint.sh"
+
+    - name: Run Test - ${{ matrix.test_group }}
+      id: run_test
+      continue-on-error: true
+      timeout-minutes: 120
+      run: |
+        docker exec pxf-cbdb-dev bash -lc "cd 
/home/gpadmin/workspace/cloudberry-pxf/automation && source 
../ci/docker/pxf-cbdb-dev/common/script/pxf-env.sh && 
../ci/docker/pxf-cbdb-dev/common/script/run_tests.sh ${{ matrix.test_group }}"
+
+    - name: Collect artifacts and generate stats
+      if: always()
+      id: collect_artifacts
+      run: |
+        mkdir -p artifacts/logs
+        TEST_GROUP="${{ matrix.test_group }}"
+        TEST_RESULT="${{ steps.run_test.outcome }}"
+
+        # Initialize counters
+        TOTAL=0
+        PASSED=0
+        FAILED=0
+        SKIPPED=0
+
+        # Copy test artifacts
+        cp -r cloudberry-pxf/automation/test_artifacts/* artifacts/ 
2>/dev/null || true
+        docker exec pxf-cbdb-dev bash -c "cp -r /usr/local/pxf/logs/* 
/tmp/pxf-logs/ 2>/dev/null || true" || true
+        docker cp pxf-cbdb-dev:/tmp/pxf-logs artifacts/logs/ 2>/dev/null || 
true
+
+        # Parse surefire reports for automation tests
+        if [[ "$TEST_GROUP" != "cli" && "$TEST_GROUP" != "server" ]]; then
+          for xml in 
cloudberry-pxf/automation/target/surefire-reports/TEST-*.xml; do
+            if [ -f "$xml" ]; then
+              tests=$(grep -oP 'tests="\K\d+' "$xml" 2>/dev/null | head -1 || 
echo "0")
+              failures=$(grep -oP 'failures="\K\d+' "$xml" 2>/dev/null | head 
-1 || echo "0")
+              errors=$(grep -oP 'errors="\K\d+' "$xml" 2>/dev/null | head -1 
|| echo "0")
+              skipped=$(grep -oP 'skipped="\K\d+' "$xml" 2>/dev/null | head -1 
|| echo "0")
+
+              TOTAL=$((TOTAL + tests))
+              FAILED=$((FAILED + failures + errors))
+              SKIPPED=$((SKIPPED + skipped))
+            fi
+          done
+          PASSED=$((TOTAL - FAILED - SKIPPED))
+        fi
+
+        # Generate stats JSON
+        cat > artifacts/test_stats.json <<EOF
+        {
+          "group": "$TEST_GROUP",
+          "result": "$TEST_RESULT",
+          "total": $TOTAL,
+          "passed": $PASSED,
+          "failed": $FAILED,
+          "skipped": $SKIPPED
+        }
+        EOF
+
+        echo "failed_count=$FAILED" >> $GITHUB_OUTPUT
+        echo "skipped_count=$SKIPPED" >> $GITHUB_OUTPUT
+        echo "Test stats for $TEST_GROUP (Rocky 10): total=$TOTAL, 
passed=$PASSED, failed=$FAILED, skipped=$SKIPPED"
+
+    - name: Cleanup containers
+      if: always()
+      run: |
+        cd cloudberry-pxf
+        docker compose -f ci/docker/pxf-cbdb-dev/rocky10/docker-compose.yml 
down -v || true
+
+    - name: Save Maven cache
+      if: always() && steps.cache-mvn.outputs.cache-hit != 'true'
+      uses: actions/cache/save@v4
+      with:
+        path: .m2
+        key: mvn-container-${{ hashFiles('cloudberry-pxf/automation/pom.xml') 
}}
+
+    - name: Upload test artifacts
+      if: always()
+      uses: actions/upload-artifact@v4
+      with:
+        name: test-results-rocky10-${{ matrix.test_group }}
+        path: artifacts/**
+        if-no-files-found: ignore
+        retention-days: 7
+
+    - name: Check test result
+      if: always()
+      run: |
+        FAILED_COUNT="${{ steps.collect_artifacts.outputs.failed_count || 0 }}"
+        SKIPPED_COUNT="${{ steps.collect_artifacts.outputs.skipped_count || 0 
}}"
+
+        if [ "${{ steps.run_test.outcome }}" == "failure" ] || [ 
"$FAILED_COUNT" -gt 0 ]; then
+          echo "Test group ${{ matrix.test_group }} (Rocky 10) failed 
(Failures: $FAILED_COUNT, Skipped: $SKIPPED_COUNT)"
+          exit 1
+        fi
+
+
   # Stage 2c: Testcontainers-based tests
   pxf-testcontainer-test:
     name: "TC Test - ${{ matrix.tc_group }} (${{ matrix.use_fdw == 'true' && 
'fdw' || 'external-table' }}, ${{ matrix.distro }})"
@@ -707,6 +994,7 @@ jobs:
         distro:
           - 'ubuntu'
           - 'rocky9'
+          - 'rocky10'
         exclude:
           - tc_group: 'pxf-extension'
             use_fdw: 'true'
@@ -871,7 +1159,7 @@ jobs:
   # Stage 3: Summary job
   test-summary:
     name: Test Summary
-    needs: [pxf-test, pxf-test-rocky9, pxf-testcontainer-test]
+    needs: [pxf-test, pxf-test-rocky9, pxf-test-rocky10, 
pxf-testcontainer-test]
     if: always()
     runs-on: ubuntu-latest
     steps:
@@ -964,10 +1252,13 @@ jobs:
 
         EXIT_CODE=0
 
-        # Ubuntu results (test-results-<group>, excluding 
test-results-rocky9-*)
+        # Ubuntu results (test-results-<group>, excluding 
test-results-rocky*-*)
         generate_os_summary "Ubuntu 22.04" "test-results-[!r]*" || EXIT_CODE=1
 
         # Rocky 9 results
         generate_os_summary "Rocky 9" "test-results-rocky9-*" || EXIT_CODE=1
 
+        # Rocky 10 results
+        generate_os_summary "Rocky 10" "test-results-rocky10-*" || EXIT_CODE=1
+
         exit $EXIT_CODE
diff --git 
a/automation/src/main/java/org/apache/cloudberry/pxf/automation/testcontainers/PXFCloudberryContainer.java
 
b/automation/src/main/java/org/apache/cloudberry/pxf/automation/testcontainers/PXFCloudberryContainer.java
index 798af68a..df7858e7 100644
--- 
a/automation/src/main/java/org/apache/cloudberry/pxf/automation/testcontainers/PXFCloudberryContainer.java
+++ 
b/automation/src/main/java/org/apache/cloudberry/pxf/automation/testcontainers/PXFCloudberryContainer.java
@@ -59,6 +59,7 @@ public class PXFCloudberryContainer extends 
GenericContainer<PXFCloudberryContai
     static {
         BASE_IMAGES.put("ubuntu", 
"apache/incubator-cloudberry:cbdb-build-ubuntu22.04-latest");
         BASE_IMAGES.put("rocky9", 
"apache/incubator-cloudberry:cbdb-build-rocky9-latest");
+        BASE_IMAGES.put("rocky10", 
"apache/incubator-cloudberry:cbdb-build-rocky10-latest");
     }
 
     public static final int CLOUDBERRY_PORT = 7000;
diff --git a/automation/src/main/resources/testcontainers/pxf-cbdb/Dockerfile 
b/automation/src/main/resources/testcontainers/pxf-cbdb/Dockerfile
index ed3c54ce..6c2f1b65 100644
--- a/automation/src/main/resources/testcontainers/pxf-cbdb/Dockerfile
+++ b/automation/src/main/resources/testcontainers/pxf-cbdb/Dockerfile
@@ -20,7 +20,10 @@
 ARG BASE_IMAGE=apache/incubator-cloudberry:cbdb-build-ubuntu22.04-latest
 FROM ${BASE_IMAGE}
 
-# Install Java 11: auto-detect OS package manager
+# Install Java 11: auto-detect OS package manager.
+# RHEL/Rocky 10 dropped OpenJDK 11 from its repos (only 21/25 remain), so take
+# it from Adoptium there and expose it as /usr/lib/jvm/java-11-openjdk, which
+# is the path script/{pxf-env,entrypoint,build_pxf}.sh resolve JAVA_BUILD to.
 RUN if command -v apt-get >/dev/null 2>&1; then \
       export DEBIAN_FRONTEND=noninteractive && \
       sudo apt-get update && \
@@ -30,8 +33,21 @@ RUN if command -v apt-get >/dev/null 2>&1; then \
         openjdk-11-jdk-headless; \
     elif command -v dnf >/dev/null 2>&1; then \
       sudo dnf install -y --allowerasing --nobest \
-        curl ca-certificates wget maven unzip openssh-server iproute sudo 
glibc-langpack-en glibc-locale-source \
-        java-11-openjdk-devel && \
+        curl ca-certificates wget maven unzip openssh-server iproute sudo \
+        glibc-langpack-en glibc-locale-source && \
+      . /etc/os-release && \
+      if [ "${VERSION_ID%%.*}" -ge 10 ]; then \
+        printf 
'[adoptium]\nname=Adoptium\nbaseurl=https://packages.adoptium.net/artifactory/rpm/rhel/%s/%s\nenabled=1\ngpgcheck=1\ngpgkey=https://packages.adoptium.net/artifactory/api/gpg/key/public\n'
 \
+          "${VERSION_ID%%.*}" "$(uname -m)" \
+          | sudo tee /etc/yum.repos.d/adoptium.repo >/dev/null && \
+        sudo dnf install -y --allowerasing --nobest temurin-11-jdk && \
+        javac_path=$(rpm -ql temurin-11-jdk | grep -m1 '/bin/javac$') && \
+        [ -n "$javac_path" ] && \
+        sudo ln -sfn "$(dirname "$(dirname "$javac_path")")" 
/usr/lib/jvm/java-11-openjdk && \
+        test -x /usr/lib/jvm/java-11-openjdk/bin/javac; \
+      else \
+        sudo dnf install -y --allowerasing --nobest java-11-openjdk-devel; \
+      fi && \
       sudo dnf clean all; \
     fi
 
diff --git 
a/automation/src/main/resources/testcontainers/pxf-cbdb/script/entrypoint.sh 
b/automation/src/main/resources/testcontainers/pxf-cbdb/script/entrypoint.sh
index 3ff2a513..7efa795d 100755
--- a/automation/src/main/resources/testcontainers/pxf-cbdb/script/entrypoint.sh
+++ b/automation/src/main/resources/testcontainers/pxf-cbdb/script/entrypoint.sh
@@ -78,11 +78,23 @@ setup_ssh() {
   sudo ssh-keygen -A
   sudo bash -c 'echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config'
   sudo mkdir -p /etc/ssh/sshd_config.d
-  sudo bash -c 'cat >/etc/ssh/sshd_config.d/pxf-automation.conf <<EOF
+  # OpenSSH 9.8+ (RHEL/Rocky 10 ships 9.9) removed DSA altogether. Naming 
ssh-dss
+  # there makes sshd reject the whole file with "Bad key types" and refuse to
+  # start, so only ask for the key types the local sshd still knows about.
+  local key_algs=ssh-rsa
+  if ssh -Q key 2>/dev/null | grep -qx ssh-dss; then
+    key_algs=ssh-rsa,ssh-dss
+  fi
+  log "sshd key types: ${key_algs}"
+  # Must sort before 40-redhat-crypto-policies.conf: sshd keeps the FIRST value
+  # it obtains for a keyword, so a drop-in read after the crypto-policy include
+  # is silently ignored. That is why Rocky 10 never actually offered ssh-rsa.
+  sudo rm -f /etc/ssh/sshd_config.d/pxf-automation.conf
+  sudo bash -c "cat >/etc/ssh/sshd_config.d/01-pxf-automation.conf <<EOF
 KexAlgorithms 
+diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1
-HostKeyAlgorithms +ssh-rsa,ssh-dss
-PubkeyAcceptedAlgorithms +ssh-rsa,ssh-dss
-EOF'
+HostKeyAlgorithms +${key_algs}
+PubkeyAcceptedAlgorithms +${key_algs}
+EOF"
   if [ "$OS_FAMILY" = "deb" ]; then
     sudo usermod -a -G sudo gpadmin
   else
@@ -105,7 +117,17 @@ EOF'
   # Ensure privilege separation user exists (required by Rocky 9 sshd)
   id sshd &>/dev/null || sudo useradd -r -d /var/empty/sshd -s /sbin/nologin 
sshd 2>/dev/null || true
   sudo mkdir -p /var/empty/sshd && sudo chmod 0755 /var/empty/sshd
-  sudo /usr/sbin/sshd -E /tmp/sshd.log || die "Failed to start sshd, check 
/tmp/sshd.log"
+  # Validate first: a rejected directive makes sshd exit without writing -E 
log,
+  # which otherwise leaves nothing to go on but "Failed to start sshd".
+  if ! sudo /usr/sbin/sshd -t 2>/tmp/sshd-config-test.log; then
+    log "ERROR sshd rejected its configuration:"
+    cat /tmp/sshd-config-test.log 2>/dev/null || true
+    die "invalid sshd configuration"
+  fi
+  sudo /usr/sbin/sshd -E /tmp/sshd.log || {
+    cat /tmp/sshd.log 2>/dev/null || true
+    die "Failed to start sshd, check /tmp/sshd.log"
+  }
   sleep 1
   if ! ss -tlnp | grep -q ':22 '; then
     log "ERROR: sshd is not listening on port 22"
diff --git a/ci/docker/pxf-cbdb-dev/common/script/entrypoint.sh 
b/ci/docker/pxf-cbdb-dev/common/script/entrypoint.sh
index b6b2bdb1..cf99df59 100755
--- a/ci/docker/pxf-cbdb-dev/common/script/entrypoint.sh
+++ b/ci/docker/pxf-cbdb-dev/common/script/entrypoint.sh
@@ -64,8 +64,20 @@ setup_locale_and_packages() {
     sudo locale-gen en_US.UTF-8 ru_RU.CP1251 ru_RU.UTF-8
     sudo update-locale LANG=en_US.UTF-8
   else
+    # RHEL/Rocky 10 dropped OpenJDK 8 and 11 from its repos (only 21/25 
remain).
+    # The image already ships both from Adoptium under the /usr/lib/jvm names
+    # detect_java_paths() expects, so only ask dnf for them on 9 and older.
+    local rpm_jdk_pkgs="java-11-openjdk-headless java-1.8.0-openjdk-headless"
+    if [ -r /etc/os-release ]; then
+      local os_major
+      os_major=$(. /etc/os-release && echo "${VERSION_ID%%.*}")
+      if [ -n "$os_major" ] && [ "$os_major" -ge 10 ] 2>/dev/null; then
+        rpm_jdk_pkgs=""
+      fi
+    fi
+    # shellcheck disable=SC2086 # word splitting is intended for the package 
list
     retry sudo dnf install -y --nobest wget maven unzip openssh-server iproute 
sudo \
-      java-11-openjdk-headless java-1.8.0-openjdk-headless \
+      $rpm_jdk_pkgs \
       glibc-langpack-en glibc-locale-source
     sudo localedef -c -i en_US -f UTF-8 en_US.UTF-8 || true
     sudo localedef -c -i ru_RU -f UTF-8 ru_RU.UTF-8 || true
@@ -88,11 +100,23 @@ setup_ssh() {
   sudo ssh-keygen -A
   sudo bash -c 'echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config'
   sudo mkdir -p /etc/ssh/sshd_config.d
-  sudo bash -c 'cat >/etc/ssh/sshd_config.d/pxf-automation.conf <<EOF
+  # OpenSSH 9.8+ (RHEL/Rocky 10 ships 9.9) removed DSA altogether. Naming 
ssh-dss
+  # there makes sshd reject the whole file with "Bad key types" and refuse to
+  # start, so only ask for the key types the local sshd still knows about.
+  local key_algs=ssh-rsa
+  if ssh -Q key 2>/dev/null | grep -qx ssh-dss; then
+    key_algs=ssh-rsa,ssh-dss
+  fi
+  log "sshd key types: ${key_algs}"
+  # Must sort before 40-redhat-crypto-policies.conf: sshd keeps the FIRST value
+  # it obtains for a keyword, so a drop-in read after the crypto-policy include
+  # is silently ignored. That is why Rocky 10 never actually offered ssh-rsa.
+  sudo rm -f /etc/ssh/sshd_config.d/pxf-automation.conf
+  sudo bash -c "cat >/etc/ssh/sshd_config.d/01-pxf-automation.conf <<EOF
 KexAlgorithms 
+diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1
-HostKeyAlgorithms +ssh-rsa,ssh-dss
-PubkeyAcceptedAlgorithms +ssh-rsa,ssh-dss
-EOF'
+HostKeyAlgorithms +${key_algs}
+PubkeyAcceptedAlgorithms +${key_algs}
+EOF"
   if [ "$OS_FAMILY" = "deb" ]; then
     sudo usermod -a -G sudo gpadmin
   else
@@ -115,7 +139,17 @@ EOF'
   # Ensure privilege separation user exists (required by Rocky 9 sshd)
   id sshd &>/dev/null || sudo useradd -r -d /var/empty/sshd -s /sbin/nologin 
sshd 2>/dev/null || true
   sudo mkdir -p /var/empty/sshd && sudo chmod 0755 /var/empty/sshd
-  sudo /usr/sbin/sshd -E /tmp/sshd.log || die "Failed to start sshd, check 
/tmp/sshd.log"
+  # Validate first: a rejected directive makes sshd exit without writing -E 
log,
+  # which otherwise leaves nothing to go on but "Failed to start sshd".
+  if ! sudo /usr/sbin/sshd -t 2>/tmp/sshd-config-test.log; then
+    log "ERROR sshd rejected its configuration:"
+    cat /tmp/sshd-config-test.log 2>/dev/null || true
+    die "invalid sshd configuration"
+  fi
+  sudo /usr/sbin/sshd -E /tmp/sshd.log || {
+    cat /tmp/sshd.log 2>/dev/null || true
+    die "Failed to start sshd, check /tmp/sshd.log"
+  }
   sleep 1
   if ! ss -tlnp | grep -q ':22 '; then
     log "ERROR: sshd is not listening on port 22"
diff --git a/ci/docker/pxf-cbdb-dev/rocky10/docker-compose.yml 
b/ci/docker/pxf-cbdb-dev/rocky10/docker-compose.yml
new file mode 100644
index 00000000..317996bc
--- /dev/null
+++ b/ci/docker/pxf-cbdb-dev/rocky10/docker-compose.yml
@@ -0,0 +1,58 @@
+# --------------------------------------------------------------------
+#
+# 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.
+#
+# --------------------------------------------------------------------
+services:
+# hadoop
+  singlecluster:
+    build:
+      context: ../../../singlecluster
+      args:
+        BASE_IMAGE: apache/incubator-cloudberry:cbdb-build-rocky10-latest
+    image: pxf/singlecluster-rocky10:3
+    container_name: pxf_singlecluster_rocky10
+    hostname: cdw
+
+  pxf-cbdb-dev:
+    image: pxf/singlecluster-rocky10:3
+    container_name: pxf-cbdb-dev
+    hostname: mdw
+    depends_on:
+      - singlecluster
+    environment:
+      # Pin container TZ so JVM's ZoneId.systemDefault() (used by
+      # ParquetTypeConverter for INT96 timestamp encode/decode) is the
+      # same across distros. Matches the PGOPTIONS timezone='GMT-1' set
+      # by run_tests.sh, keeping the Parquet-vs-PG timestamp path
+      # internally consistent.
+      TZ: Etc/GMT-1
+    ports:
+      - "2222:22"
+    volumes:
+      - ../../../../../cloudberry-pxf:/home/gpadmin/workspace/cloudberry-pxf
+      - ../../../../../cloudberry:/home/gpadmin/workspace/cloudberry
+      # Share host's Maven local repo with the container so mvn dependency
+      # downloads survive across runs via actions/cache. Neutralises transient
+      # 5xx from Maven Central and single-source repos (top-q.co.il for
+      # JSystem) once the cache is warm.
+      - ../../../../../.m2:/home/gpadmin/.m2
+    command: ["tail", "-f", "/dev/null"]
+
+networks:
+  default:
+    name: pxf-cbdb-ci
diff --git a/ci/docker/pxf-cbdb-dev/rocky10/script/build_cloudberry_rpm.sh 
b/ci/docker/pxf-cbdb-dev/rocky10/script/build_cloudberry_rpm.sh
new file mode 100755
index 00000000..7d9da3cf
--- /dev/null
+++ b/ci/docker/pxf-cbdb-dev/rocky10/script/build_cloudberry_rpm.sh
@@ -0,0 +1,118 @@
+#!/bin/bash
+# --------------------------------------------------------------------
+#
+# 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.
+#
+# --------------------------------------------------------------------
+set -euo pipefail
+
+# Cloudberry RPM Package Build Script for Rocky 10
+CLOUDBERRY_VERSION="${CLOUDBERRY_VERSION:-99.0.0}"
+CLOUDBERRY_BUILD="${CLOUDBERRY_BUILD:-1}"
+INSTALL_PREFIX="${INSTALL_PREFIX:-/usr/local/cloudberry-db}"
+WORKSPACE="${WORKSPACE:-$HOME/workspace}"
+CLOUDBERRY_SRC="${WORKSPACE}/cloudberry"
+
+echo "=== Cloudberry RPM Package Build ==="
+echo "Version: ${CLOUDBERRY_VERSION}"
+echo "Build: ${CLOUDBERRY_BUILD}"
+echo "Install Prefix: ${INSTALL_PREFIX}"
+echo "Source: ${CLOUDBERRY_SRC}"
+
+# Clean previous installation
+rm -rf "${INSTALL_PREFIX}"
+mkdir -p "${INSTALL_PREFIX}"
+
+# Copy xerces-c shared libraries required by ORCA
+mkdir -p "${INSTALL_PREFIX}/lib"
+cp -v /usr/local/xerces-c/lib/libxerces-c.so \
+      /usr/local/xerces-c/lib/libxerces-c-3.*.so \
+      "${INSTALL_PREFIX}/lib/"
+
+# Build Cloudberry using official build scripts
+export SRC_DIR="${CLOUDBERRY_SRC}"
+export CPPFLAGS="${CPPFLAGS:-} -I/usr/local/xerces-c/include"
+export LDFLAGS="${LDFLAGS:-} -L${INSTALL_PREFIX}/lib"
+mkdir -p "${SRC_DIR}/build-logs"
+cd "${CLOUDBERRY_SRC}"
+./devops/build/automation/cloudberry/scripts/configure-cloudberry.sh
+./devops/build/automation/cloudberry/scripts/build-cloudberry.sh
+
+# Copy LICENSE
+cp LICENSE "${INSTALL_PREFIX}/"
+
+# Create RPM build structure
+RPM_BUILD_DIR="${WORKSPACE}/cloudberry-rpm"
+mkdir -p "${RPM_BUILD_DIR}"/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
+RPM_INSTALL_ROOT="${RPM_BUILD_DIR}/BUILDROOT/apache-cloudberry-db-${CLOUDBERRY_VERSION}-${CLOUDBERRY_BUILD}.x86_64"
+mkdir -p "${RPM_INSTALL_ROOT}${INSTALL_PREFIX}"
+
+# Copy installed files
+cp -a "${INSTALL_PREFIX}"/* "${RPM_INSTALL_ROOT}${INSTALL_PREFIX}/"
+
+# Create spec file
+cat > "${RPM_BUILD_DIR}/SPECS/cloudberry-db.spec" << EOF
+Name: apache-cloudberry-db
+Version: ${CLOUDBERRY_VERSION}
+Release: ${CLOUDBERRY_BUILD}%{?dist}
+Summary: Apache Cloudberry Database
+License: Apache-2.0
+Group: Applications/Databases
+AutoReqProv: no
+
+%description
+Apache Cloudberry is a massively parallel processing (MPP) database
+built on PostgreSQL for analytics and data warehousing.
+
+%install
+mkdir -p %{buildroot}${INSTALL_PREFIX}
+cp -a ${RPM_INSTALL_ROOT}${INSTALL_PREFIX}/* %{buildroot}${INSTALL_PREFIX}/
+
+%files
+${INSTALL_PREFIX}
+
+%post
+if ! id -u gpadmin >/dev/null 2>&1; then
+    useradd -m -s /bin/bash gpadmin
+fi
+chown -R gpadmin:gpadmin ${INSTALL_PREFIX}
+echo "Apache Cloudberry Database installed successfully"
+
+%clean
+rm -rf %{buildroot}
+EOF
+
+# Build RPM package.
+#
+# RHEL/Rocky 10 added check-rpaths to the default %install post-processing 
chain
+# (RHEL 9 never runs it), and it aborts the build over RUNPATHs that Cloudberry
+# sets on purpose: ${INSTALL_PREFIX}/lib on its own libraries, /usr/lib64 on 
the
+# plpython3 modules and /ext/python/lib on plpython3.so. This RPM exists only 
to
+# install Cloudberry into a CI container and is never published, so turn the
+# rpath QA off entirely and keep this lane behaving like the Rocky 9 one.
+QA_RPATHS=$(( 0x0001|0x0002|0x0004|0x0008|0x0010|0x0020 )) \
+  rpmbuild --define "_topdir ${RPM_BUILD_DIR}" -bb 
"${RPM_BUILD_DIR}/SPECS/cloudberry-db.spec"
+
+RPM_FILE=$(find "${RPM_BUILD_DIR}/RPMS" -name "*.rpm" | head -1)
+echo "=== RPM Package Created ==="
+ls -lh "${RPM_FILE}"
+rpm -qpi "${RPM_FILE}"
+
+# Copy RPM to output directory
+cp "${RPM_FILE}" "${RPM_BUILD_DIR}/"
+
+echo "=== Build Complete ==="
diff --git a/ci/singlecluster/Dockerfile b/ci/singlecluster/Dockerfile
index e5b6566b..f28d43ef 100644
--- a/ci/singlecluster/Dockerfile
+++ b/ci/singlecluster/Dockerfile
@@ -20,7 +20,12 @@
 ARG BASE_IMAGE=apache/incubator-cloudberry:cbdb-build-ubuntu22.04-latest
 FROM ${BASE_IMAGE}
 
-# Install Java 8 & 11: auto-detect OS package manager
+# Install Java 8 & 11: auto-detect OS package manager.
+# Hadoop 3.1.2 runs on JDK 8 (JAVA_HADOOP) and the PXF server builds with
+# JDK 11 (JAVA_BUILD), so both are needed on every distro. RHEL/Rocky 10
+# dropped OpenJDK 8 and 11 from its repos (only 21/25 remain), so take them
+# from Adoptium there and expose them under the same /usr/lib/jvm names the
+# CI scripts look for (see ci/docker/pxf-cbdb-dev/common/script/utils.sh).
 RUN if command -v apt-get >/dev/null 2>&1; then \
       export DEBIAN_FRONTEND=noninteractive && \
       sudo apt-get update && \
@@ -29,10 +34,27 @@ RUN if command -v apt-get >/dev/null 2>&1; then \
         openjdk-8-jdk-headless \
         openjdk-11-jdk-headless; \
     elif command -v dnf >/dev/null 2>&1; then \
-      sudo dnf install -y --allowerasing --nobest \
-        curl ca-certificates \
-        java-1.8.0-openjdk-devel \
-        java-11-openjdk-devel && \
+      sudo dnf install -y --allowerasing --nobest curl ca-certificates && \
+      . /etc/os-release && \
+      if [ "${VERSION_ID%%.*}" -ge 10 ]; then \
+        printf 
'[adoptium]\nname=Adoptium\nbaseurl=https://packages.adoptium.net/artifactory/rpm/rhel/%s/%s\nenabled=1\ngpgcheck=1\ngpgkey=https://packages.adoptium.net/artifactory/api/gpg/key/public\n'
 \
+          "${VERSION_ID%%.*}" "$(uname -m)" \
+          | sudo tee /etc/yum.repos.d/adoptium.repo >/dev/null && \
+        sudo dnf install -y --allowerasing --nobest temurin-8-jdk 
temurin-11-jdk && \
+        for v in 8 11; do \
+          javac_path=$(rpm -ql "temurin-${v}-jdk" | grep -m1 '/bin/javac$') && 
\
+          [ -n "$javac_path" ] || exit 1; \
+          jdk_home=$(dirname "$(dirname "$javac_path")"); \
+          if [ "$v" = 8 ]; then link=java-1.8.0-openjdk; else 
link="java-${v}-openjdk"; fi; \
+          sudo ln -sfn "$jdk_home" "/usr/lib/jvm/${link}"; \
+        done && \
+        test -x /usr/lib/jvm/java-1.8.0-openjdk/bin/javac && \
+        test -x /usr/lib/jvm/java-11-openjdk/bin/javac; \
+      else \
+        sudo dnf install -y --allowerasing --nobest \
+          java-1.8.0-openjdk-devel \
+          java-11-openjdk-devel; \
+      fi && \
       sudo dnf clean all; \
     fi
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to