This is an automated email from the ASF dual-hosted git repository.
voonhous pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hudi.git
The following commit(s) were added to refs/heads/master by this push:
new 5ba8d7ca1915 test(trino): build the E2E server image from the pin
(#19953)
5ba8d7ca1915 is described below
commit 5ba8d7ca1915b8734a44ea4bdd822463140a3ff4
Author: voonhous <[email protected]>
AuthorDate: Wed Sep 16 17:22:52 2026 +0800
test(trino): build the E2E server image from the pin (#19953)
The Trino E2E job built the connector against the trinodb/trino commit
in trino.sha but ran it on the released trinodb/trino image. Master
tracks Trino master, so the two always differed, and an SPI drift gate
skipped every test step while the job still reported success. The
suite had not run on master since the pin started tracking Trino
master.
Build the server image from the pinned commit instead, so the server
and the connector always come from the same commit, and drop the gate:
- build_trino_server_image.sh packages trinodb/trino at trino.sha and
builds the image with Trino's own core/docker/build.sh. It leaves
the heap to trino's .mvn/jvm.config.
- The E2E job builds that image on every run and bakes the plugin on
top through build_image.sh --base-image. Neither the image nor the
io.trino artifacts are cached: the scheduled delete-all-caches job
empties both before a later run can restore them.
- The Dockerfile takes TRINO_BASE_IMAGE, with the released image as
the default for quick local runs.
- overlay-entrypoint.sh runs bin/launcher directly with bash builtins
only: Trino master removed run-trino and moved to a base image
without grep or find.
Closes #19954.
---
.github/workflows/hudi_trino_e2e.yml | 116 ++++++-------------------
docker/README.md | 6 +-
docker/trino/Dockerfile | 6 +-
docker/trino/build_image.sh | 23 +++--
docker/trino/build_trino_server_image.sh | 145 +++++++++++++++++++++++++++++++
docker/trino/overlay-entrypoint.sh | 32 ++++++-
hudi-trino/README.md | 19 ++--
pom.xml | 3 +-
release/release_guide.md | 7 +-
9 files changed, 247 insertions(+), 110 deletions(-)
diff --git a/.github/workflows/hudi_trino_e2e.yml
b/.github/workflows/hudi_trino_e2e.yml
index e6269c94bd47..f6ebacadba90 100644
--- a/.github/workflows/hudi_trino_e2e.yml
+++ b/.github/workflows/hudi_trino_e2e.yml
@@ -16,9 +16,10 @@ on:
# which compiles the whole module, so a break
# anywhere in it fails this pipeline -- not just
# under integ2/.
- # pom.xml owns trino.version / trino.sha /
trino.e2e.version:
- # the shim pom's parent, the plugin dir path and
the
- # server image version below are all derived
from them.
+ # pom.xml owns trino.version / trino.sha (the shim pom's
parent,
+ # the plugin dir path, and the commit both the
server
+ # image and the connector are built from) and
+ # trino.e2e.version (trino-jdbc for the ITs).
- 'hudi-trino/**'
- 'docker/trino/**'
- 'docker/compose/docker-compose_hadoop340_hive2310_spark402*'
@@ -64,8 +65,9 @@ jobs:
# hudi-trino at HEAD, assembles the plugin dir via the in-repo shim
# (docker/trino/shim, standing in for the not-yet-released upstream
# trinodb/trino plugin/trino-hudi shim), bakes it into a local
- # apachehudi/hudi-trino-e2e image on top of the released trino.e2e.version
- # server, and runs ITTestTrino* against the spark402 compose stack (the
only
+ # apachehudi/hudi-trino-e2e image on top of a Trino server image built from
+ # the same pinned trinodb/trino commit (so server and connector always
+ # match), and runs ITTestTrino* against the spark402 compose stack (the
only
# pair with the trinocoordinator service).
runs-on: ubuntu-latest
steps:
@@ -76,69 +78,16 @@ jobs:
set -euo pipefail
TRINO_SHA=$(sed -n 's|.*<trino.sha>\(.*\)</trino.sha>.*|\1|p'
pom.xml)
TRINO_VERSION=$(sed -n
's|.*<trino.version>\(.*\)</trino.version>.*|\1|p' pom.xml)
- E2E_VERSION=$(sed -n
's|.*<trino.e2e.version>\(.*\)</trino.e2e.version>.*|\1|p' pom.xml)
- # sed -n ...p exits 0 on no match; an empty sha would make the
commits API answer
- # for the default branch and silently skip the suite, so fail loudly
instead.
- if [ -z "$TRINO_SHA" ] || [ -z "$TRINO_VERSION" ] || [ -z
"$E2E_VERSION" ]; then
- echo "ERROR: could not read
trino.sha/trino.version/trino.e2e.version from pom.xml" >&2
+ # sed -n ...p exits 0 on no match; an empty sha would check out
trinodb/trino's default
+ # branch and key the caches on nothing, so fail loudly instead.
+ if [ -z "$TRINO_SHA" ] || [ -z "$TRINO_VERSION" ]; then
+ echo "ERROR: could not read trino.sha/trino.version from pom.xml"
>&2
exit 1
fi
- echo "Connector builds at $TRINO_VERSION ($TRINO_SHA); server image
is $E2E_VERSION"
+ echo "Server image and connector both build at $TRINO_VERSION
($TRINO_SHA)"
echo "trino_sha=$TRINO_SHA" >> "$GITHUB_OUTPUT"
echo "trino_version=$TRINO_VERSION" >> "$GITHUB_OUTPUT"
- echo "e2e_version=$E2E_VERSION" >> "$GITHUB_OUTPUT"
- - name: SPI drift gate
- id: spi-drift
- # The plugin is built at the pin but loaded by the released
trino.e2e.version server, so
- # any SPI / filesystem change between the two can make the image
unbootable. Skip the run
- # instead of reporting a failure that no connector change caused.
- env:
- GH_TOKEN: ${{ github.token }}
- TRINO_SHA: ${{ steps.trino-pin.outputs.trino_sha }}
- TRINO_VERSION: ${{ steps.trino-pin.outputs.trino_version }}
- E2E_VERSION: ${{ steps.trino-pin.outputs.e2e_version }}
- run: |
- set -euo pipefail
- # Per-path commit queries, NOT the compare API: compare caps its
file list at 300
- # and a single Trino release cycle already exceeds that, so a capped
compare would
- # flag every pin more than a release old as drifted. The commits API
is uncapped;
- # any commit reachable from the pin that touched a boundary-crossing
path after the
- # released tag's commit date (excluding the tag commit itself) is
drift. Existence
- # is enough, so the first page settles it -- truncation cannot yield
a false pass.
- # Gated paths are the surfaces where server-built and pin-built
classes meet:
- # core/trino-spi (the server provides it to the plugin classloader),
and the
- # HdfsFileSystemLoader contract, where bundled
trino-filesystem-manager loads the
- # server image's version-matched hdfs jar set (see
docker/trino/Dockerfile) whose
- # HdfsClassLoader then delegates the exact packages
io.trino.filesystem and
- # io.trino.memory.context back to the plugin's bundled copies -- so
those two libs
- # cross the boundary precisely BECAUSE they are bundled.
- TAG_SHA=$(gh api "repos/trinodb/trino/commits/${E2E_VERSION}" --jq
.sha)
- TAG_DATE=$(gh api "repos/trinodb/trino/commits/${E2E_VERSION}" --jq
.commit.committer.date)
- DRIFTED=false
- for p in core/trino-spi lib/trino-filesystem
lib/trino-filesystem-manager lib/trino-hdfs lib/trino-memory-context; do
- # Assign before iterating: a failing substitution in the for-list
would not trip
- # set -e, and gh api prints the error body to stdout, so a
transient API error
- # would otherwise iterate over error JSON and silently skip the
suite.
- SHAS=$(gh api
"repos/trinodb/trino/commits?sha=${TRINO_SHA}&path=${p}&since=${TAG_DATE}" --jq
'.[].sha')
- for c in $SHAS; do
- if [ "$c" != "$TAG_SHA" ]; then
- echo "Boundary-crossing change under ${p}: ${c}"
- DRIFTED=true
- fi
- done
- done
- echo "drifted=$DRIFTED" >> "$GITHUB_OUTPUT"
- if [ "$DRIFTED" = "true" ]; then
- {
- echo "## Trino E2E skipped: SPI drift window"
- echo ""
- echo "The connector is built against trinodb/trino
\`${TRINO_VERSION}\` (\`${TRINO_SHA}\`), while the"
- echo "e2e server image is released Trino \`${E2E_VERSION}\`. SPI
/ filesystem paths changed between"
- echo "the two, so this run is skipped until the pin and the
released version re-align."
- } >> "$GITHUB_STEP_SUMMARY"
- fi
- name: Free disk space
- if: steps.spi-drift.outputs.drifted != 'true'
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/lib/android
@@ -146,14 +95,12 @@ jobs:
sudo rm -rf /usr/local/share/boost
docker system prune --all --force --volumes
- name: Pre-pull compose images (fails fast if not published)
- if: steps.spi-drift.outputs.drifted != 'true'
run: |
# Surface a missing sparkadhoc image before the long Maven install.
The
# remaining stack images are pulled by docker-compose at test time;
the
# trino image is built locally below, never pulled.
docker pull
apachehudi/hudi-hadoop_3.4.0-hive_2.3.10-sparkadhoc_4.0.2:latest
- name: Set up JDK 17
- if: steps.spi-drift.outputs.drifted != 'true'
uses: actions/setup-java@v5
with:
java-version: '17'
@@ -161,50 +108,45 @@ jobs:
architecture: x64
cache: maven
- name: Build and install Hudi artifacts (JDK 17)
- if: steps.spi-drift.outputs.drifted != 'true'
# Full reactor: the compose containers mount the workspace and the
tests
# use bundles staged by the -Pintegration-tests build (e.g.
# docker/hoodie/hadoop/hive_base/target/hoodie-spark-bundle.jar).
run:
mvn clean install -T 2 $SCALA_PROFILE -Dspark4.0 -Dflink1.20
-Pintegration-tests -DskipTests=true -Ddocker.compose.skip=true $MVN_ARGS
- name: Set up JDK 25
- if: steps.spi-drift.outputs.drifted != 'true'
uses: actions/setup-java@v5
with:
java-version: '25'
distribution: 'temurin'
cache: maven
- name: Purge Trino artifacts from the local m2
- if: steps.spi-drift.outputs.drifted != 'true'
# Artifacts an older pin left behind carry the same SNAPSHOT
coordinates as the current ones.
run: rm -rf ~/.m2/repository/io/trino
# The connector and the shim assembly resolve io.trino from the pinned
trinodb/trino
# commit: Trino publishes no SNAPSHOT artifacts, so nothing here comes
from Central.
- - name: Restore Trino artifacts for the pinned commit
- id: trino-m2
- if: steps.spi-drift.outputs.drifted != 'true'
- uses: actions/cache@v4
- with:
- path: ~/.m2/repository/io/trino
- key: trino-m2-v2-${{ hashFiles('scripts/trino/bootstrap_trino.sh')
}}-${{ steps.trino-pin.outputs.trino_sha }}
- # No restore-keys on purpose: a partial restore from another pin
collides on the
- # same SNAPSHOT coordinates and poisons the build.
+ # Not cached, for the same reason as the server image below.
- name: Checkout trinodb/trino at the pinned commit
- if: steps.spi-drift.outputs.drifted != 'true' &&
steps.trino-m2.outputs.cache-hit != 'true'
uses: actions/checkout@v5
with:
repository: trinodb/trino
ref: ${{ steps.trino-pin.outputs.trino_sha }}
path: trino-src
- name: Build Trino artifacts from source (JDK 25)
- if: steps.spi-drift.outputs.drifted != 'true' &&
steps.trino-m2.outputs.cache-hit != 'true'
run: scripts/trino/bootstrap_trino.sh trino-src --skip-checkout
+ # Built on every run, from the same commit as the connector. Not cached:
the scheduled
+ # workflow's delete-all-caches job removes Actions caches before a later
run can restore them.
+ - name: Build the Trino server image from the pinned commit (JDK 25)
+ env:
+ TRINO_SHA: ${{ steps.trino-pin.outputs.trino_sha }}
+ run: docker/trino/build_trino_server_image.sh trino-src --image
"hudi-trino-server:${TRINO_SHA}" --arch amd64
+ - name: Free the Trino build tree
+ # The server image is in the local docker store and io.trino is in the
m2 by now;
+ # reclaim the disk before the connector build and the compose stack.
+ run: rm -rf trino-src
- name: Build hudi-trino connector (JDK 25)
- if: steps.spi-drift.outputs.drifted != 'true'
run:
mvn $MVN_ARGS -Phudi-trino -pl hudi-trino install
-Dmaven.test.skip=true
- name: Assemble trino-hudi plugin dir via in-repo shim (JDK 25)
- if: steps.spi-drift.outputs.drifted != 'true'
# package, NOT install: installing would shadow the real
# io.trino:trino-hudi release coordinates in the local m2 (the shim pom
# also hard-disables install via maven.install.skip).
@@ -217,23 +159,21 @@ jobs:
echo "Building shim against hudi version: $HUDI_VERSION"
mvn $MVN_ARGS -f docker/trino/shim/pom.xml clean package -DskipTests
-Ddep.hudi.version="$HUDI_VERSION"
- name: Build apachehudi/hudi-trino-e2e image
- if: steps.spi-drift.outputs.drifted != 'true'
- # The plugin dir is named after the version it was built at; the
server underneath is
- # the released trino.e2e.version.
+ # The plugin dir is named after trino.version; the server underneath
is the image built
+ # from the same pinned commit above.
env:
TRINO_VERSION: ${{ steps.trino-pin.outputs.trino_version }}
- E2E_VERSION: ${{ steps.trino-pin.outputs.e2e_version }}
+ TRINO_SHA: ${{ steps.trino-pin.outputs.trino_sha }}
run: |
PLUGIN_DIR="docker/trino/shim/target/trino-hudi-${TRINO_VERSION}"
# trino-maven-plugin 24 emits only the zip; explode it into the
plugin dir layout.
unzip -o -q "${PLUGIN_DIR}.zip" -d docker/trino/shim/target
- docker/trino/build_image.sh --plugin-dir "$PLUGIN_DIR"
--trino-version "${E2E_VERSION}"
+ docker/trino/build_image.sh --plugin-dir "$PLUGIN_DIR" --base-image
"hudi-trino-server:${TRINO_SHA}"
# Sanity: the shim must have produced a populated plugin dir with a
# service descriptor jar, or Trino cannot load the plugin at boot.
echo "plugin dir jar count: $(ls "$PLUGIN_DIR" | wc -l)"
ls "$PLUGIN_DIR"/*services*.jar
- name: Smoke-boot the Trino image standalone
- if: steps.spi-drift.outputs.drifted != 'true'
# Catches image-level boot failures (plugin load errors, bad etc/
config)
# ~30 min before the IT step would, with the full boot log on screen.
# --hostname trinocoordinator makes the baked discovery.uri
self-resolve.
@@ -260,7 +200,6 @@ jobs:
fi
docker rm -f trino-smoke
- name: Set up JDK 17 (restore for the IT run)
- if: steps.spi-drift.outputs.drifted != 'true'
# setup-java resets JAVA_HOME on each call; hudi-integ-test needs 17.
uses: actions/setup-java@v5
with:
@@ -268,7 +207,6 @@ jobs:
distribution: 'temurin'
architecture: x64
- name: Run Trino E2E ITs (JDK 17)
- if: steps.spi-drift.outputs.drifted != 'true'
run: |
# -DskipITs=false overrides the spark4.0 profile's skipITs=true
default
# (see root pom.xml). -Dcompose.profiles=trino starts the
profile-gated
diff --git a/docker/README.md b/docker/README.md
index a3e6aa1fc8ed..bcf8949c51d0 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -207,8 +207,10 @@ changes are needed for the current amd64 plus arm64 image
set in this repository
## Trino E2E image - `/trino`
The Trino E2E stack does not use the `hoodie/hadoop` image tree.
`docker/trino/` builds
-`apachehudi/hudi-trino-e2e` directly on top of the official `trinodb/trino`
image at the
-root pom's `trino.e2e.version`, baking in a locally-assembled native
`trino-hudi` plugin
+`apachehudi/hudi-trino-e2e` on top of a Trino server image built from the
pinned
+`trinodb/trino` commit (`trino.sha`, via
`docker/trino/build_trino_server_image.sh`; this is
+what CI does) or, for quick local runs, the released
`trinodb/trino:<trino.e2e.version>`,
+baking in a locally-assembled native `trino-hudi` plugin
directory and the E2E catalog config (`connector.name=hudi`, metastore at
`thrift://hivemetastore:9083`).
diff --git a/docker/trino/Dockerfile b/docker/trino/Dockerfile
index 891a41aa1ec7..752dfc93308d 100644
--- a/docker/trino/Dockerfile
+++ b/docker/trino/Dockerfile
@@ -15,8 +15,12 @@
# specific language governing permissions and limitations
# under the License.
+# CI passes TRINO_BASE_IMAGE: a server image built from the pinned trino.sha
+# (build_trino_server_image.sh). The released-image default is for quick local
runs and
+# only matches the connector when the pin is a release.
ARG TRINO_VERSION=483
-FROM trinodb/trino:${TRINO_VERSION}
+ARG TRINO_BASE_IMAGE=trinodb/trino:${TRINO_VERSION}
+FROM ${TRINO_BASE_IMAGE}
USER root
diff --git a/docker/trino/build_image.sh b/docker/trino/build_image.sh
index 9a2303ff7117..fc9bddbd7df5 100755
--- a/docker/trino/build_image.sh
+++ b/docker/trino/build_image.sh
@@ -20,10 +20,13 @@
# docker/trino/shim/target/trino-hudi-<v>, see docker/trino/shim/pom.xml) is
# staged into the build context at docker/trino/plugin/ (gitignored), then
# baked into the image.
-# Usage: ./build_image.sh --plugin-dir <path> [--trino-version <v>]
[--image-tag <t>]
-# Typical: ./build_image.sh --plugin-dir "$(dirname
"$0")/shim/target/trino-hudi-<trino.version>"
-# Note: --trino-version is the released Trino server image to build on top of
-# (the root pom's trino.e2e.version), not the version the plugin was built at.
+# Usage: ./build_image.sh --plugin-dir <path> [--base-image <image>]
[--trino-version <v>] [--image-tag <t>]
+# Typical: ./build_image.sh --plugin-dir "$(dirname
"$0")/shim/target/trino-hudi-<trino.version>" \
+# --base-image hudi-trino-server:<trino.sha>
+# Note: --base-image (e.g. the output of build_trino_server_image.sh, built
from the pinned
+# trino.sha) takes precedence over --trino-version. --trino-version is the
released-image
+# fallback, trinodb/trino:<v> (default: the root pom's trino.e2e.version), not
the version
+# the plugin was built at; it only boots when the pin's SPI matches that
release.
set -e
@@ -33,6 +36,7 @@ SCRIPT_DIR=$(cd $(dirname "$0") && pwd)
# Default values. The server version defaults to the root pom's
trino.e2e.version (the
# nightly pin-advance job keeps that current; a literal default here would
rot).
PLUGIN_DIR=""
+BASE_IMAGE=""
TRINO_VERSION=$(sed -n
's|.*<trino.e2e.version>\(.*\)</trino.e2e.version>.*|\1|p'
"$SCRIPT_DIR/../../pom.xml")
IMAGE_TAG="latest"
@@ -40,6 +44,7 @@ IMAGE_TAG="latest"
while [[ "$#" -gt 0 ]]; do
case $1 in
--plugin-dir) PLUGIN_DIR="$2"; shift ;;
+ --base-image) BASE_IMAGE="$2"; shift ;;
--trino-version) TRINO_VERSION="$2"; shift ;;
--image-tag) IMAGE_TAG="$2"; shift ;;
*) echo "Unknown parameter passed: $1"; exit 1 ;;
@@ -73,8 +78,14 @@ rm -rf "$STAGE_DIR"
cp -r "$PLUGIN_DIR" "$STAGE_DIR"
IMAGE="apachehudi/hudi-trino-e2e:${IMAGE_TAG}"
-echo "Building $IMAGE (TRINO_VERSION=${TRINO_VERSION})"
-docker build --build-arg TRINO_VERSION="${TRINO_VERSION}" -t "$IMAGE"
"$SCRIPT_DIR"
+BUILD_ARGS=(--build-arg TRINO_VERSION="${TRINO_VERSION}")
+if [ -n "$BASE_IMAGE" ]; then
+ echo "Building $IMAGE on base image ${BASE_IMAGE}"
+ BUILD_ARGS+=(--build-arg TRINO_BASE_IMAGE="${BASE_IMAGE}")
+else
+ echo "Building $IMAGE on released base image trinodb/trino:${TRINO_VERSION}"
+fi
+docker build "${BUILD_ARGS[@]}" -t "$IMAGE" "$SCRIPT_DIR"
# Clean up the staged plugin dir
echo "Cleaning up staged plugin dir '$STAGE_DIR'"
diff --git a/docker/trino/build_trino_server_image.sh
b/docker/trino/build_trino_server_image.sh
new file mode 100755
index 000000000000..8be4d5494967
--- /dev/null
+++ b/docker/trino/build_trino_server_image.sh
@@ -0,0 +1,145 @@
+#!/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.
+
+##
+## Builds a local Trino server image from the trinodb/trino commit pinned by
+## <trino.sha> in the root pom, so the server and the hudi-trino connector
baked
+## on top of it (docker/trino/build_image.sh --base-image) come from the same
+## commit. The image is local-only and never published.
+##
+## Usage: build_trino_server_image.sh <path-to-trino-checkout> [--image
<name:tag>] [--arch <amd64|arm64>]
+##
+## <path-to-trino-checkout> a trinodb/trino git checkout at exactly
<trino.sha>
+## --image <name:tag> tag for the resulting image
+## (default: hudi-trino-server:<trino.sha>)
+## --arch <amd64|arm64> image architecture (default: the Docker
daemon's arch)
+##
+## Requires JDK 25, jq and docker on PATH.
+##
+
+set -euo pipefail
+
+HUDI_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
+
+TRINO_REPO=""
+IMAGE=""
+ARCH=""
+
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --image)
+ IMAGE="$2"
+ shift 2
+ ;;
+ --arch)
+ ARCH="$2"
+ shift 2
+ ;;
+ -h|--help)
+ grep '^##' "$0" | sed 's/^## \{0,1\}//'
+ exit 0
+ ;;
+ *)
+ if [[ -z "$TRINO_REPO" ]]; then
+ TRINO_REPO="$1"
+ shift
+ else
+ echo "ERROR: unexpected argument: $1" >&2
+ exit 1
+ fi
+ ;;
+ esac
+done
+
+if [[ -z "$TRINO_REPO" || ! -d "$TRINO_REPO/.git" && ! -f "$TRINO_REPO/.git"
]]; then
+ echo "ERROR: first argument must be a trinodb/trino git checkout" >&2
+ echo "Usage: $0 <path-to-trino-checkout> [--image <name:tag>] [--arch
<amd64|arm64>]" >&2
+ exit 1
+fi
+TRINO_REPO="$(cd "$TRINO_REPO" && pwd)"
+
+# Keep each property on one line in the root pom; this sed depends on it.
+TRINO_SHA=$(sed -n 's|.*<trino.sha>\(.*\)</trino.sha>.*|\1|p'
"$HUDI_ROOT/pom.xml")
+TRINO_VERSION=$(sed -n 's|.*<trino.version>\(.*\)</trino.version>.*|\1|p'
"$HUDI_ROOT/pom.xml")
+if [[ -z "$TRINO_SHA" || -z "$TRINO_VERSION" ]]; then
+ echo "ERROR: could not read <trino.sha>/<trino.version> from
$HUDI_ROOT/pom.xml" >&2
+ exit 1
+fi
+
+# JDK gate: trino at head enforces JDK 25.
+JAVA_MAJOR=$(java -version 2>&1 | awk -F[\".] '/version/ {print $2}')
+if [[ "$JAVA_MAJOR" != "25" ]]; then
+ echo "ERROR: JDK 25 required to build trino (found major version:
${JAVA_MAJOR:-unknown})." >&2
+ echo "Hint: export JAVA_HOME=\$(/usr/libexec/java_home -v 25)" >&2
+ exit 1
+fi
+
+for tool in jq docker; do
+ if ! command -v "$tool" > /dev/null 2>&1; then
+ echo "ERROR: $tool is required on PATH (trino's core/docker/build.sh needs
jq and docker)" >&2
+ exit 1
+ fi
+done
+
+ACTUAL_SHA=$(git -C "$TRINO_REPO" rev-parse HEAD)
+if [[ "$ACTUAL_SHA" != "$TRINO_SHA" ]]; then
+ echo "ERROR: $TRINO_REPO is at $ACTUAL_SHA, but the root pom pins
<trino.sha>$TRINO_SHA</trino.sha>." >&2
+ echo "Hint: git -C $TRINO_REPO -c advice.detachedHead=false checkout
--detach $TRINO_SHA" >&2
+ exit 1
+fi
+
+ACTUAL_VERSION=$("$TRINO_REPO/mvnw" -q -N -f "$TRINO_REPO/pom.xml"
help:evaluate -Dexpression=project.version -DforceStdout)
+if [[ "$ACTUAL_VERSION" != "$TRINO_VERSION" ]]; then
+ echo "ERROR: trino at pinned sha $TRINO_SHA has version $ACTUAL_VERSION, but
the root pom says <trino.version>$TRINO_VERSION</trino.version>." >&2
+ echo "The pin properties must advance together; fix the pom or your
checkout." >&2
+ exit 1
+fi
+
+if [[ -z "$ARCH" ]]; then
+ ARCH=$(docker version --format '{{.Server.Arch}}')
+fi
+if [[ "$ARCH" != "amd64" && "$ARCH" != "arm64" ]]; then
+ echo "ERROR: --arch must be amd64 or arm64 (got: ${ARCH:-empty})" >&2
+ exit 1
+fi
+
+if [[ -z "$IMAGE" ]]; then
+ IMAGE="hudi-trino-server:${TRINO_SHA}"
+fi
+
+# No MAVEN_OPTS default: trino's .mvn/jvm.config asks for -Xmx8192m and
Maven's launcher
+# appends MAVEN_OPTS after it, so a default here would halve the heap for the
build below.
+
+# Why the whole repo (minus docs) instead of -pl :trino-server -am: the
server's provisio
+# descriptors pull in plugin zips that are not Maven dependencies, so -am
would not build
+# them. Why the full trino-server tarball instead of trino-server-core: only
the full server
+# packages plugin/hudi, whose hdfs/ jar set docker/trino/Dockerfile carries
over.
+# package, NOT install: ~/.m2/repository/io/trino belongs to
scripts/trino/bootstrap_trino.sh.
+echo "Building the trino server at $TRINO_SHA (version $TRINO_VERSION)"
+(cd "$TRINO_REPO" && ./mvnw package -B -DskipTests -Dair.check.skip-all=true
-Dmaven.source.skip=true -T1C \
+ -pl '!:trino-docs')
+
+# Without -r, trino's build.sh builds from the locally built server tarball
and CLI jar and
+# tags <prefix>:<project.version>-<arch>; -x skips its image tests.
+BUILD_PREFIX="hudi-trino-server-build"
+echo "Building the trino server image for $ARCH"
+(cd "$TRINO_REPO/core/docker" && ./build.sh -a "$ARCH" -t "$BUILD_PREFIX" -x)
+
+docker tag "${BUILD_PREFIX}:${TRINO_VERSION}-${ARCH}" "$IMAGE"
+docker rmi "${BUILD_PREFIX}:${TRINO_VERSION}-${ARCH}" > /dev/null
+
+echo "Done: $IMAGE"
diff --git a/docker/trino/overlay-entrypoint.sh
b/docker/trino/overlay-entrypoint.sh
index 2ba9b517e26a..19e07ffcaf48 100755
--- a/docker/trino/overlay-entrypoint.sh
+++ b/docker/trino/overlay-entrypoint.sh
@@ -24,6 +24,11 @@
# image's baked-in trino-hudi plugin with it (rm -rf then copy), so plugin
# iterations need only a rebuild of that dir plus a container restart, not a
# docker image rebuild. Otherwise the image-baked plugin is used as-is.
+#
+# This runs on released images (<= 483, which still shipped bin/run-trino) and
on
+# images built from the pinned trino.sha (after trinodb/trino f0d1f3c982e,
which
+# removed run-trino in favor of launcher in the CMD, on a hardened base image
+# without grep or find). Keep it bash builtins only, apart from cp/rm/mkdir.
set -euo pipefail
OVERLAY=/opt/hudi-plugin-overlay
@@ -32,7 +37,14 @@ PLUGIN_DIR=/usr/lib/trino/plugin/hudi
# The overlay counts as present only if it holds at least one jar: the compose
# default mount is docker/trino/empty-overlay, whose .gitkeep must not trigger
# a wipe of the baked-in plugin.
-if [ -d "$OVERLAY" ] && [ -n "$(find "$OVERLAY" -name '*.jar' -print -quit
2>/dev/null)" ]; then
+# dotglob mirrors find, which also descends into hidden dirs.
+OVERLAY_JARS=()
+if [ -d "$OVERLAY" ]; then
+ shopt -s globstar nullglob dotglob
+ OVERLAY_JARS=("$OVERLAY"/**/*.jar)
+ shopt -u globstar nullglob dotglob
+fi
+if [ "${#OVERLAY_JARS[@]}" -gt 0 ]; then
echo "Applying trino-hudi plugin overlay from $OVERLAY (fully replacing
$PLUGIN_DIR)"
rm -rf "$PLUGIN_DIR"
mkdir -p "$PLUGIN_DIR"
@@ -49,4 +61,20 @@ if [ ! -d "$PLUGIN_DIR/hdfs" ] && [ -d /opt/hudi-hdfs-lib ];
then
cp -r /opt/hudi-hdfs-lib "$PLUGIN_DIR/hdfs"
fi
-exec /usr/lib/trino/bin/run-trino
+# Inlined from the removed bin/run-trino wrapper. The node.id check mirrors its
+# `grep -s -q 'node.id' /etc/trino/node.properties`: a missing or unreadable
file
+# means not set.
+launcher_opts=(--etc-dir /etc/trino)
+NODE_ID_SET=false
+if [ -r /etc/trino/node.properties ]; then
+ while IFS= read -r line || [ -n "$line" ]; do
+ if [[ "$line" == *node.id* ]]; then
+ NODE_ID_SET=true
+ break
+ fi
+ done < /etc/trino/node.properties
+fi
+if [ "$NODE_ID_SET" != "true" ]; then
+ launcher_opts+=("-Dnode.id=${HOSTNAME}")
+fi
+exec /usr/lib/trino/bin/launcher run "${launcher_opts[@]}" "$@"
diff --git a/hudi-trino/README.md b/hudi-trino/README.md
index 763eade3cc70..d9bd21680f9d 100644
--- a/hudi-trino/README.md
+++ b/hudi-trino/README.md
@@ -66,9 +66,9 @@ project mirroring the upstream `trinodb/trino`
`plugin/trino-hudi` shim planned
RFC-105 (not yet released upstream). CI runs the same flow via
`.github/workflows/hudi_trino_e2e.yml`.
-The plugin is built at the pinned `trino.version` while the server image is
the released
-`trino.e2e.version`; CI auto-skips the suite during SPI drift windows (SPI or
filesystem changes
-between the two).
+CI builds the Trino server image from the pinned `trino.sha`
+(`docker/trino/build_trino_server_image.sh`), so the plugin and the server
always come from
+the same commit.
Local flow (after bootstrap):
@@ -91,9 +91,16 @@ TRINO_VERSION=$(sed -n
's|.*<trino.version>\(.*\)</trino.version>.*|\1|p' pom.xm
mvn -f docker/trino/shim/pom.xml clean package -DskipTests
-Ddep.hudi.version="$HUDI_VERSION"
unzip -o -q "docker/trino/shim/target/trino-hudi-$TRINO_VERSION.zip" -d
docker/trino/shim/target # trino-maven-plugin 24 emits only the zip
-# 4. Build the Trino image (locally tagged; never published). The base server
defaults to
-# trino.e2e.version; pass --trino-version to override it.
-docker/trino/build_image.sh --plugin-dir
"docker/trino/shim/target/trino-hudi-$TRINO_VERSION"
+# 3a. Optional, JDK 25: build the server image from a trinodb/trino checkout
at trino.sha
+# (the same one bootstrap used). Builds the whole trino repo, so it takes
a while.
+docker/trino/build_trino_server_image.sh /path/to/trino
+TRINO_SHA=$(sed -n 's|.*<trino.sha>\(.*\)</trino.sha>.*|\1|p' pom.xml)
+
+# 4. Build the Trino image (locally tagged; never published) on the server
from 3a.
+# Without --base-image it falls back to the released
trinodb/trino:<trino.e2e.version>
+# (or --trino-version), which only boots when the pin's SPI matches that
release.
+docker/trino/build_image.sh --plugin-dir
"docker/trino/shim/target/trino-hudi-$TRINO_VERSION" \
+ --base-image "hudi-trino-server:$TRINO_SHA"
# 5. JDK 17: run the suite (only the spark402 compose pair has the trino
service)
mvn verify -pl hudi-integ-test -Dscala-2.13 -Dscala.binary.version=2.13
-Dspark4.0 \
diff --git a/pom.xml b/pom.xml
index 11dc55c3355f..787a7b445eb6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -140,7 +140,8 @@
<presto.version>0.273</presto.version>
<!-- trino.sha is the exact trinodb/trino commit hudi-trino builds
against; its project
version must equal trino.version and the two advance only together.
trino.e2e.version is
- the latest released Trino (e2e server image, trino-jdbc for the integ
tests); the nightly
+ the latest released Trino (trino-jdbc for the integ tests, and the
fallback server image
+ for local E2E runs; CI's E2E server image is built from trino.sha);
the nightly
pin-advance job refreshes all three, humans merge its bot/trino-pin
branch. Keep each
property on one line: the workflows and
scripts/trino/bootstrap_trino.sh read them with sed. -->
<trino.version>484-SNAPSHOT</trino.version>
diff --git a/release/release_guide.md b/release/release_guide.md
index ae49b2d281a7..e48599cd01b9 100644
--- a/release/release_guide.md
+++ b/release/release_guide.md
@@ -306,7 +306,8 @@ Source Release step) -- otherwise the voted tarball ships a
`-SNAPSHOT` Trino pi
and revert them forward on the release branch only, never on master.
4. On the release branch set `trino.version=NNN`, `trino.sha=TAG_SHA` and
`trino.e2e.version=NNN` in the root
pom, the `<parent>` version in `docker/trino/shim/pom.xml`, and the `ARG
TRINO_VERSION` default in
- `docker/trino/Dockerfile` (`build_image.sh` reads `trino.e2e.version` from
the root pom). Re-check SPI-surface-coupled
+ `docker/trino/Dockerfile` (`trino.e2e.version` is the trino-jdbc version
and the released fallback base image that
+ `build_image.sh` uses without `--base-image`). Re-check SPI-surface-coupled
dependency scopes against `NNN` (e.g. `jts-core` is `provided` because it
joined the Trino SPI surface in 482;
the shim's SpiDependencyChecker fails the build loudly if a scope no longer
matches the target release).
5. Verify the released Trino resolves from Central against an empty local
repository
@@ -337,8 +338,8 @@ Source Release step) -- otherwise the voted tarball ships a
`-SNAPSHOT` Trino pi
Locally the script exits 1 on drift and prints the same table.
If it reports drift, bump the matching version properties in
`hudi-trino/pom.xml` on the release branch and rerun
until it is clean.
-7. CI and the E2E workflow then run with zero SPI drift; the staging deploy
flow in "Build a release candidate"
- is unchanged.
+7. The E2E workflow builds its Trino server image from the pinned tag commit,
as on master; the staging deploy flow
+ in "Build a release candidate" is unchanged.
## Verify that a Release Build Works