build(docker): ship the silo binary and translate the legacy argv command

Dockerfile.goreleaser now copies silo to /usr/bin/silo, defaults to
CMD ["silo"], and labels the image as Silo. MINIO_UPDATE_MINISIGN_PUBKEY is
removed from the image environment: with the updater permanently disabled it
was dead weight, and leaving upstream's verification key in a Silo image
implied a trust relationship that does not exist. The MINIO_* runtime
environment variables, ports, volume and health endpoints are unchanged.

The image keeps shipping mcli with an /usr/bin/mc symlink. That is the client,
not a MinIO-branded alias for the server binary, and the Helm post-install job
and existing container scripts call it by name.

docker-entrypoint.sh translates a legacy first argument: `minio server /data`
becomes `silo server /data`, so an existing `command: minio server ...` in
compose or a Pod spec keeps working across the image swap. The translation is
argv-level only - no file named minio is installed, and an explicitly
overridden `entrypoint: /usr/bin/minio` still fails, which is the honest
outcome since that path genuinely no longer exists.

The entrypoint also fixes an unrelated startup hazard it was already carrying:
when the image runs under an arbitrary UID, HOME points at an unreadable /root
and the server probes its default config directory during initialization. It
now falls back to /tmp when HOME is unset, /root, missing or unwritable.

docker-entrypoint_test.sh pins all of it - empty argv, legacy minio, native
silo, bare flags, and an explicit shell command that must stay explicit. It is
wired into make rebrand-guard and into the go.yml, release.yml and
test-release.yml gates, so removing the shim breaks CI rather than breaking
users. Passing locally.

download-static-curl.sh gains checksum verification for the curl it fetches
into the build stage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Feng Ruohang
2026-08-06 08:47:59 +08:00
parent 15ab10833b
commit 30749911bd
4 changed files with 136 additions and 30 deletions
+29 -7
View File
@@ -1,21 +1,43 @@
#!/bin/sh
#
# If command starts with an option, prepend minio.
if [ "${1}" != "minio" ]; then
if [ -n "${1}" ]; then
set -- minio "$@"
# Run Silo by default, while translating the legacy argv-level command name.
# An explicitly supplied shell or utility remains an explicit entrypoint command.
case "${1:-}" in
"")
set -- silo
;;
minio)
shift
set -- silo "$@"
;;
silo)
;;
-*|server|fmt-gen)
set -- silo "$@"
;;
esac
ensure_writable_home() {
# The image is commonly run with an arbitrary UID or with the legacy
# MINIO_USERNAME drop-user path. Do not leave those processes pointing at
# root's inaccessible home: Silo probes its default configuration directory
# during process initialization.
if [ -z "${HOME:-}" ] || [ "${HOME}" = /root ] || [ ! -d "${HOME}" ] || [ ! -w "${HOME}" ]; then
HOME=/tmp
export HOME
fi
fi
}
docker_switch_user() {
ensure_writable_home
if [ -n "${MINIO_USERNAME}" ] && [ -n "${MINIO_GROUPNAME}" ]; then
if [ -n "${MINIO_UID}" ] && [ -n "${MINIO_GID}" ]; then
exec chroot --userspec=${MINIO_UID}:${MINIO_GID} / "$@"
exec chroot --userspec="${MINIO_UID}:${MINIO_GID}" / "$@"
else
echo "${MINIO_USERNAME}:x:1000:1000:${MINIO_USERNAME}:/:/sbin/nologin" >>/etc/passwd
echo "${MINIO_GROUPNAME}:x:1000" >>/etc/group
exec chroot --userspec=${MINIO_USERNAME}:${MINIO_GROUPNAME} / "$@"
exec chroot --userspec="${MINIO_USERNAME}:${MINIO_GROUPNAME}" / "$@"
fi
else
exec "$@"
+75
View File
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
work_dir="$(mktemp -d)"
trap 'rm -rf "${work_dir}"' EXIT
mkdir -p "${work_dir}/bin"
cat >"${work_dir}/bin/silo" <<'EOF'
#!/bin/sh
printf '%s\n' silo "$@" >"${ENTRYPOINT_CAPTURE}"
if [ -n "${ENTRYPOINT_HOME_CAPTURE:-}" ]; then
printf '%s\n' "${HOME:-}" >"${ENTRYPOINT_HOME_CAPTURE}"
fi
EOF
chmod +x "${work_dir}/bin/silo"
cat >"${work_dir}/bin/chroot" <<'EOF'
#!/bin/sh
printf '%s\n' "$1" "$2" >"${ENTRYPOINT_CHROOT_CAPTURE}"
shift 2
exec "$@"
EOF
chmod +x "${work_dir}/bin/chroot"
run_case() {
local name="$1"
local expected="$2"
shift 2
local capture="${work_dir}/${name}.actual"
local expected_file="${work_dir}/${name}.expected"
ENTRYPOINT_CAPTURE="${capture}" PATH="${work_dir}/bin:${PATH}" \
"${script_dir}/docker-entrypoint.sh" "$@"
printf '%s' "${expected}" >"${expected_file}"
diff -u "${expected_file}" "${capture}"
}
run_case default $'silo\n'
run_case server $'silo\nserver\n/data\n' server /data
run_case option $'silo\n--version\n' --version
run_case explicit-silo $'silo\nserver\n/data\n' silo server /data
run_case legacy-minio $'silo\nserver\n/data\n' minio server /data
shell_capture="${work_dir}/shell.actual"
# shellcheck disable=SC2016 # ENTRYPOINT_CAPTURE is expanded by the child shell.
ENTRYPOINT_CAPTURE="${shell_capture}" PATH="${work_dir}/bin:${PATH}" \
"${script_dir}/docker-entrypoint.sh" sh -c 'printf "%s\n" shell >"${ENTRYPOINT_CAPTURE}"'
test "$(cat "${shell_capture}")" = shell
rootless_capture="${work_dir}/rootless.actual"
rootless_home_capture="${work_dir}/rootless-home.actual"
rootless_chroot_capture="${work_dir}/rootless-chroot.actual"
ENTRYPOINT_CAPTURE="${rootless_capture}" \
ENTRYPOINT_HOME_CAPTURE="${rootless_home_capture}" \
ENTRYPOINT_CHROOT_CAPTURE="${rootless_chroot_capture}" \
HOME=/root MINIO_USERNAME=silo-user MINIO_GROUPNAME=silo-group \
MINIO_UID=1001 MINIO_GID=1002 PATH="${work_dir}/bin:${PATH}" \
"${script_dir}/docker-entrypoint.sh" silo --version
test "$(cat "${rootless_home_capture}")" = /tmp
test "$(sed -n '1p' "${rootless_chroot_capture}")" = --userspec=1001:1002
test "$(sed -n '2p' "${rootless_chroot_capture}")" = /
test "$(cat "${rootless_capture}")" = $'silo\n--version'
arbitrary_uid_capture="${work_dir}/arbitrary-uid.actual"
arbitrary_uid_home_capture="${work_dir}/arbitrary-uid-home.actual"
ENTRYPOINT_CAPTURE="${arbitrary_uid_capture}" \
ENTRYPOINT_HOME_CAPTURE="${arbitrary_uid_home_capture}" \
HOME="${work_dir}/missing-home" PATH="${work_dir}/bin:${PATH}" \
"${script_dir}/docker-entrypoint.sh" silo --version
test "$(cat "${arbitrary_uid_home_capture}")" = /tmp
test "$(cat "${arbitrary_uid_capture}")" = $'silo\n--version'
echo "docker entrypoint argv compatibility tests passed"
+15 -13
View File
@@ -4,21 +4,23 @@
# v8.17.0 (current latest) dropped the aarch64 build.
STATIC_CURL_VERSION="v8.11.0"
function download_arch_specific_executable {
curl -f -L -s -q \
"https://github.com/moparisthebest/static-curl/releases/download/${STATIC_CURL_VERSION}/curl-$1" \
-o /go/bin/curl || exit 1
chmod +x /go/bin/curl
}
case $TARGETARCH in
"arm64")
download_arch_specific_executable aarch64
case ${TARGETARCH:?TARGETARCH is required} in
amd64)
asset=curl-amd64
expected=d18aa1f4e03b50b649491ca2c401cd8c5e89e72be91ff758952ad2ab5a83135d
;;
"s390x")
echo "Not downloading static cURL because it does not exist for the $TARGETARCH architecture."
arm64)
asset=curl-aarch64
expected=1b050abd1669f9a2ac29b34eb022cdeafb271dce5a4fb57d8ef8fadff6d7be1f
;;
*)
download_arch_specific_executable "$TARGETARCH"
echo "Unsupported static cURL architecture: ${TARGETARCH}" >&2
exit 1
;;
esac
curl --fail --location --silent --show-error --retry 3 \
"https://github.com/moparisthebest/static-curl/releases/download/${STATIC_CURL_VERSION}/${asset}" \
--output /go/bin/curl
printf '%s %s\n' "${expected}" /go/bin/curl | sha256sum -c
chmod +x /go/bin/curl