chore: import upstream snapshot with attribution
This commit is contained in:
+266
@@ -0,0 +1,266 @@
|
||||
#!/bin/sh
|
||||
# Shell-level unit test for vllm-nonroot-entrypoint.sh.
|
||||
#
|
||||
# Runs on the host (no Docker, no GPU) by stubbing `vllm` with a shim that
|
||||
# dumps its env + argv instead of actually serving. Exercises the wrapper's
|
||||
# HOME/USER fallback behavior that can't be easily tested from buildkite
|
||||
# (which would need a GPU to run `vllm serve --help`).
|
||||
#
|
||||
# Usage:
|
||||
# bash docker/entrypoints/test_vllm_nonroot_entrypoint.sh
|
||||
# Exits non-zero on the first failed assertion.
|
||||
|
||||
set -eu
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
WRAPPER="${SCRIPT_DIR}/vllm-nonroot-entrypoint.sh"
|
||||
|
||||
if [ ! -x "$WRAPPER" ]; then
|
||||
echo "FAIL: wrapper not found or not executable: $WRAPPER" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WORKDIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$WORKDIR"' EXIT
|
||||
|
||||
# Stub `vllm` on PATH. It dumps env + argv + cwd to stdout so we can assert.
|
||||
mkdir -p "$WORKDIR/bin"
|
||||
cat > "$WORKDIR/bin/vllm" <<'EOF'
|
||||
#!/bin/sh
|
||||
echo "ARGV=$*"
|
||||
echo "HOME=${HOME-__unset__}"
|
||||
echo "USER=${USER-__unset__}"
|
||||
echo "LOGNAME=${LOGNAME-__unset__}"
|
||||
echo "PWD=$(pwd)"
|
||||
EOF
|
||||
chmod +x "$WORKDIR/bin/vllm"
|
||||
|
||||
run_wrapper() {
|
||||
# Usage: run_wrapper <output_file> <env_kv>... -- <wrapper_arg>...
|
||||
_out="$1"; shift
|
||||
_env=""
|
||||
while [ "${1:-}" != "--" ]; do
|
||||
_env="$_env $1"; shift
|
||||
done
|
||||
shift
|
||||
env -i PATH="$WORKDIR/bin:/usr/bin:/bin" $_env "$WRAPPER" "$@" > "$_out"
|
||||
}
|
||||
|
||||
fail() { echo "FAIL: $*" >&2; echo "--- stdout ---" >&2; cat "$1" >&2; exit 1; }
|
||||
|
||||
expect_default_home() {
|
||||
_out="$1"
|
||||
_case="$2"
|
||||
if [ -w /home/vllm ]; then
|
||||
expected_home="/home/vllm"
|
||||
grep -q "^HOME=$expected_home\$" "$_out" \
|
||||
|| fail "$_out" "$_case: HOME not set to $expected_home"
|
||||
else
|
||||
expected_home="/tmp/vllm-home.XXXXXX"
|
||||
grep -Eq '^HOME=/tmp/vllm-home\.[^/]+$' "$_out" \
|
||||
|| fail "$_out" "$_case: HOME not set to $expected_home"
|
||||
fi
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Case 1: writable HOME and USER both set -> wrapper must leave them alone.
|
||||
# -----------------------------------------------------------------------------
|
||||
case1_home="$WORKDIR/case1-home"
|
||||
mkdir -p "$case1_home"
|
||||
out="$WORKDIR/case1.out"
|
||||
run_wrapper "$out" "HOME=$case1_home" "USER=alice" "LOGNAME=alice" -- --model foo
|
||||
grep -q "^HOME=$case1_home\$" "$out" || fail "$out" "case1: HOME not preserved"
|
||||
grep -q "^USER=alice\$" "$out" || fail "$out" "case1: USER not preserved"
|
||||
grep -q "^LOGNAME=alice\$" "$out" || fail "$out" "case1: LOGNAME not preserved"
|
||||
grep -q "^ARGV=serve --model foo\$" "$out" || fail "$out" "case1: ARGV wrong"
|
||||
echo "PASS: case1 (writable HOME + USER preserved)"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Case 2: HOME unset -> falls back to /home/vllm if writable, else
|
||||
# /tmp/vllm-home.XXXXXX.
|
||||
# -----------------------------------------------------------------------------
|
||||
# The wrapper checks whether the real /home/vllm exists and is writable. On
|
||||
# dev machines /home/vllm typically does NOT exist, so the
|
||||
# wrapper should fall to /tmp/vllm-home.XXXXXX.
|
||||
out="$WORKDIR/case2.out"
|
||||
run_wrapper "$out" -- --model bar
|
||||
expect_default_home "$out" "case2"
|
||||
grep -q "^USER=vllm\$" "$out" || fail "$out" "case2: USER not defaulted to vllm"
|
||||
grep -q "^LOGNAME=vllm\$" "$out" || fail "$out" "case2: LOGNAME not defaulted to vllm"
|
||||
grep -q "^ARGV=serve --model bar\$" "$out" || fail "$out" "case2: ARGV wrong"
|
||||
echo "PASS: case2 (unset HOME falls back to $expected_home, USER defaulted)"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Case 3: HOME set but unwritable -> must also fall back.
|
||||
# -----------------------------------------------------------------------------
|
||||
ro_home="$WORKDIR/ro-home"
|
||||
mkdir -p "$ro_home"
|
||||
chmod 0500 "$ro_home"
|
||||
out="$WORKDIR/case3.out"
|
||||
run_wrapper "$out" "HOME=$ro_home" -- --model baz
|
||||
expect_default_home "$out" "case3"
|
||||
grep -q "^USER=vllm\$" "$out" || fail "$out" "case3: USER not defaulted"
|
||||
chmod 0700 "$ro_home"
|
||||
echo "PASS: case3 (unwritable HOME overridden)"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Case 4: USER set but LOGNAME unset -> LOGNAME mirrors USER.
|
||||
# -----------------------------------------------------------------------------
|
||||
case4_home="$WORKDIR/case4-home"
|
||||
mkdir -p "$case4_home"
|
||||
out="$WORKDIR/case4.out"
|
||||
run_wrapper "$out" "HOME=$case4_home" "USER=carol" -- --model qux
|
||||
grep -q "^USER=carol\$" "$out" || fail "$out" "case4: USER not preserved"
|
||||
grep -q "^LOGNAME=carol\$" "$out" || fail "$out" "case4: LOGNAME not mirrored from USER"
|
||||
echo "PASS: case4 (LOGNAME mirrors USER when unset)"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Case 5: /etc/passwd is writable AND the current UID is not in it -> wrapper
|
||||
# appends a synthetic entry. Uses the VLLM_PASSWD_FILE test hook so we don't
|
||||
# touch the real /etc/passwd.
|
||||
# -----------------------------------------------------------------------------
|
||||
fake_passwd="$WORKDIR/fake-passwd"
|
||||
: > "$fake_passwd" # empty file, current UID definitely not present
|
||||
case5_home="$WORKDIR/case5-home"
|
||||
mkdir -p "$case5_home"
|
||||
out="$WORKDIR/case5.out"
|
||||
run_wrapper "$out" "HOME=$case5_home" "VLLM_PASSWD_FILE=$fake_passwd" -- --model foo
|
||||
current_uid="$(id -u)"
|
||||
current_gid="$(id -g)"
|
||||
expected_line="vllm:x:${current_uid}:${current_gid}:vllm:${case5_home}:/bin/bash"
|
||||
grep -Fx "$expected_line" "$fake_passwd" > /dev/null \
|
||||
|| { echo "FAIL: case5: expected line not found in fake passwd:"; echo " expected: $expected_line"; echo " file contents:"; cat "$fake_passwd"; exit 1; }
|
||||
echo "PASS: case5 (passwd entry appended for arbitrary UID)"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Case 6: /etc/passwd is writable but current UID already has an entry ->
|
||||
# wrapper must NOT duplicate the entry.
|
||||
# -----------------------------------------------------------------------------
|
||||
fake_passwd="$WORKDIR/fake-passwd-prepopulated"
|
||||
printf 'vllm:x:%s:%s:vllm:/home/vllm:/bin/bash\n' "$current_uid" "$current_gid" > "$fake_passwd"
|
||||
out="$WORKDIR/case6.out"
|
||||
run_wrapper "$out" "HOME=$case5_home" "VLLM_PASSWD_FILE=$fake_passwd" -- --model foo
|
||||
line_count="$(wc -l < "$fake_passwd")"
|
||||
# NOTE: wc may count 0 or 1 depending on trailing newline; accept 1.
|
||||
# More robust: count lines matching our UID.
|
||||
uid_lines="$(grep -c ":${current_uid}:" "$fake_passwd" || true)"
|
||||
[ "$uid_lines" = "1" ] \
|
||||
|| { echo "FAIL: case6: expected exactly one entry for UID $current_uid, got $uid_lines"; cat "$fake_passwd"; exit 1; }
|
||||
echo "PASS: case6 (existing passwd entry not duplicated)"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Case 7: /etc/passwd is NOT writable -> wrapper must NOT crash, just skip.
|
||||
# Skipped when running as root, because root's DAC override means [ -w ... ]
|
||||
# is always true regardless of mode bits -- the case can't be simulated.
|
||||
# In the real deployment (non-root UID inside the container) this IS the
|
||||
# relevant behavior and is what `_passwd_file is not writable` encodes.
|
||||
# -----------------------------------------------------------------------------
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
echo "SKIP: case7 (running as root; DAC override makes unwritable check meaningless)"
|
||||
else
|
||||
fake_passwd="$WORKDIR/ro-passwd"
|
||||
: > "$fake_passwd"
|
||||
chmod 0444 "$fake_passwd"
|
||||
out="$WORKDIR/case7.out"
|
||||
run_wrapper "$out" "HOME=$case5_home" "VLLM_PASSWD_FILE=$fake_passwd" -- --model foo
|
||||
# File must remain empty (no write happened) and the wrapper exec'd
|
||||
# `vllm serve` successfully (stdout contains ARGV line).
|
||||
[ ! -s "$fake_passwd" ] \
|
||||
|| { echo "FAIL: case7: RO passwd file was modified"; cat "$fake_passwd"; exit 1; }
|
||||
grep -q "^ARGV=serve --model foo\$" "$out" || fail "$out" "case7: wrapper didn't exec vllm"
|
||||
chmod 0600 "$fake_passwd"
|
||||
echo "PASS: case7 (unwritable passwd file tolerated)"
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Case 8: caller's writable CWD is preserved — wrapper must NOT chdir to HOME
|
||||
# when cwd is usable. Protects relative-path workflows like
|
||||
# `docker run -w /models ... --model ./llama.gguf`.
|
||||
# -----------------------------------------------------------------------------
|
||||
case8_home="$WORKDIR/case8-home"
|
||||
mkdir -p "$case8_home"
|
||||
case8_cwd="$WORKDIR/case8-cwd"
|
||||
mkdir -p "$case8_cwd"
|
||||
out="$WORKDIR/case8.out"
|
||||
(cd "$case8_cwd" && run_wrapper "$out" "HOME=$case8_home" "USER=alice" "LOGNAME=alice" -- --model ./relpath)
|
||||
grep -q "^PWD=$case8_cwd\$" "$out" \
|
||||
|| fail "$out" "case8: writable cwd not preserved (got $(grep '^PWD=' "$out"))"
|
||||
grep -q "^ARGV=serve --model \\./relpath\$" "$out" \
|
||||
|| fail "$out" "case8: relative argv not preserved"
|
||||
echo "PASS: case8 (writable cwd preserved; relative argv still resolves from caller's cwd)"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Case 9: read-only cwd is ALSO preserved. A caller who mounts a read-only
|
||||
# model directory at the container's cwd (e.g. `docker run -w /models` with
|
||||
# /models bind-mounted ro) expects relative argv like `--model ./foo.gguf`
|
||||
# to resolve against /models. An earlier version of this wrapper rewrote
|
||||
# read-only cwd to $HOME and broke that workflow; this case guards against
|
||||
# the regression returning.
|
||||
# -----------------------------------------------------------------------------
|
||||
case9_home="$WORKDIR/case9-home"
|
||||
mkdir -p "$case9_home"
|
||||
case9_ro="$WORKDIR/case9-ro"
|
||||
mkdir -p "$case9_ro"
|
||||
chmod 0555 "$case9_ro"
|
||||
out="$WORKDIR/case9.out"
|
||||
(cd "$case9_ro" && run_wrapper "$out" "HOME=$case9_home" "USER=alice" "LOGNAME=alice" -- --model ./foo)
|
||||
grep -q "^PWD=$case9_ro\$" "$out" \
|
||||
|| fail "$out" "case9: read-only cwd was rewritten (got $(grep '^PWD=' "$out"))"
|
||||
grep -q "^ARGV=serve --model \\./foo\$" "$out" \
|
||||
|| fail "$out" "case9: relative argv not preserved"
|
||||
chmod 0700 "$case9_ro"
|
||||
echo "PASS: case9 (read-only cwd preserved; relative argv still resolves from caller's cwd)"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Case 10: truly inaccessible cwd (no search bit) DOES fall back to $HOME.
|
||||
# Skipped as root because DAC override lets root cd into 0000 directories.
|
||||
# -----------------------------------------------------------------------------
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
echo "SKIP: case10 (running as root; DAC override makes inaccessible cwd untestable)"
|
||||
else
|
||||
case10_home="$WORKDIR/case10-home"
|
||||
mkdir -p "$case10_home"
|
||||
case10_cwd="$WORKDIR/case10-cwd"
|
||||
mkdir -p "$case10_cwd"
|
||||
out="$WORKDIR/case10.out"
|
||||
# Make cwd genuinely inaccessible (mode 0000 = no search bit -> cd .
|
||||
# fails with EACCES). Use absolute paths for chmod so our own test
|
||||
# cleanup still works without needing search perm on the dir.
|
||||
(
|
||||
cd "$case10_cwd"
|
||||
chmod 0000 "$case10_cwd"
|
||||
run_wrapper "$out" "HOME=$case10_home" "USER=alice" "LOGNAME=alice" -- --model foo
|
||||
)
|
||||
chmod 0700 "$case10_cwd"
|
||||
grep -q "^PWD=$case10_home\$" "$out" \
|
||||
|| fail "$out" "case10: inaccessible cwd not overridden to HOME (got $(grep '^PWD=' "$out"))"
|
||||
echo "PASS: case10 (inaccessible cwd falls back to \$HOME)"
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Case 11: if /tmp cannot create a private fallback dir, wrapper uses /tmp as
|
||||
# the last-resort HOME instead of leaving HOME empty under set -eu.
|
||||
# -----------------------------------------------------------------------------
|
||||
if [ -w /home/vllm ]; then
|
||||
echo "SKIP: case11 (/home/vllm is writable; mktemp fallback path is not used)"
|
||||
else
|
||||
cat > "$WORKDIR/bin/mktemp" <<'EOF'
|
||||
#!/bin/sh
|
||||
exit 1
|
||||
EOF
|
||||
chmod +x "$WORKDIR/bin/mktemp"
|
||||
|
||||
out="$WORKDIR/case11.out"
|
||||
run_wrapper "$out" -- --model no-mktemp
|
||||
rm -f "$WORKDIR/bin/mktemp"
|
||||
|
||||
grep -q "^HOME=/tmp\$" "$out" \
|
||||
|| fail "$out" "case11: mktemp failure did not fall back to /tmp"
|
||||
grep -q "^USER=vllm\$" "$out" || fail "$out" "case11: USER not defaulted"
|
||||
grep -q "^LOGNAME=vllm\$" "$out" || fail "$out" "case11: LOGNAME not defaulted"
|
||||
grep -q "^ARGV=serve --model no-mktemp\$" "$out" || fail "$out" "case11: ARGV wrong"
|
||||
echo "PASS: case11 (mktemp failure falls back to /tmp)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "ALL CASES PASSED."
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
#!/bin/sh
|
||||
# Entrypoint wrapper for the opt-in `vllm-openai-nonroot` image.
|
||||
#
|
||||
# The image also ships a `vllm` user (UID 2000, GID 0) with HOME /home/vllm
|
||||
# and a group-0-writable home directory. When the container is launched with
|
||||
# `--user 2000:0` (or any other UID in group 0) the passwd entry is enough on
|
||||
# its own: Docker picks up HOME=/home/vllm, getpass.getuser() resolves to
|
||||
# "vllm", and every cache dir (HF, Triton, Inductor, vLLM, Numba, Outlines)
|
||||
# that defaults to `$HOME/.cache/...` lands in a writable location.
|
||||
#
|
||||
# This wrapper exists for the *arbitrary-UID* case (e.g. OpenShift's
|
||||
# `runAsUser: 1000540000` Restricted Pod Security Standard) where the caller
|
||||
# UID is not in /etc/passwd at all. In that case:
|
||||
# * $HOME may be unset or resolve to "/" (unwritable).
|
||||
# * getpass.getuser() falls back to pwd.getpwuid() -> KeyError.
|
||||
#
|
||||
# The wrapper re-points $HOME to /home/vllm when writable, /tmp/vllm-home.XXXXXX
|
||||
# otherwise, and defaults $USER to "vllm" so the pwd-lookup path is never
|
||||
# taken. Everything else is forwarded to `vllm serve`.
|
||||
#
|
||||
# Non-empty caller-set env vars (HOME, USER, LOGNAME) are preserved, so
|
||||
# existing K8s manifests and `docker run -e ...` keep working unchanged.
|
||||
# Unset or empty values fall through to the wrapper's defaults, matching
|
||||
# what shell code typically expects from "unset".
|
||||
|
||||
set -eu
|
||||
|
||||
if [ -z "${HOME:-}" ] || [ ! -w "${HOME}" ]; then
|
||||
if [ -w /home/vllm ]; then
|
||||
export HOME=/home/vllm
|
||||
else
|
||||
if _h="$(mktemp -d /tmp/vllm-home.XXXXXX 2>/dev/null)"; then
|
||||
export HOME="$_h"
|
||||
chmod 0700 "$HOME" 2>/dev/null || true
|
||||
else
|
||||
export HOME=/tmp
|
||||
fi
|
||||
unset _h
|
||||
fi
|
||||
fi
|
||||
|
||||
# Preserve the caller's cwd whenever it's still usable. A read-only mount
|
||||
# (e.g. `docker run -w /models ... --model ./llama.gguf` where /models is
|
||||
# the user's model share) is a legitimate, usable cwd — vllm only needs to
|
||||
# *read* relative paths from there. We only fall back to $HOME when the
|
||||
# cwd itself is truly inaccessible (no search bit, deleted inode, mount
|
||||
# gone, etc.), which is when `cd .` actually fails.
|
||||
#
|
||||
# This is the accessibility check, not a writability check; the latter
|
||||
# would silently rewrite cwd for any read-only workflow and break relative
|
||||
# argv like `--model ./llama.gguf`, `--chat-template ./t.jinja`, relative
|
||||
# TLS cert paths, etc.
|
||||
if ! cd . 2>/dev/null; then
|
||||
cd "$HOME"
|
||||
fi
|
||||
|
||||
# getpass.getuser() prefers $USER/$LOGNAME/etc. before hitting getpwuid();
|
||||
# setting it here makes the "UID not in passwd" path a no-op for everything
|
||||
# in the process tree.
|
||||
if [ -z "${USER:-}" ]; then
|
||||
export USER=vllm
|
||||
fi
|
||||
if [ -z "${LOGNAME:-}" ]; then
|
||||
export LOGNAME="$USER"
|
||||
fi
|
||||
|
||||
# Shell-level tooling (`whoami`, bash's `\u` prompt, `id -un`, `sudo`) does
|
||||
# NOT consult $USER; it calls getpwuid(geteuid()) directly. For arbitrary
|
||||
# runtime UIDs in OpenShift-style deploys this returns "I have no name!".
|
||||
# If /etc/passwd is group-0 writable (set at build time) and doesn't yet
|
||||
# have an entry for this UID, append a synthetic one so every downstream
|
||||
# consumer sees a consistent "vllm" identity.
|
||||
#
|
||||
# We parse the passwd file directly instead of calling `getent` because
|
||||
# the container's NSS is typically just files anyway, and this lets us
|
||||
# unit-test via the VLLM_PASSWD_FILE hook (undocumented; production uses
|
||||
# /etc/passwd).
|
||||
_passwd_file="${VLLM_PASSWD_FILE:-/etc/passwd}"
|
||||
_uid="$(id -u)"
|
||||
if [ -w "$_passwd_file" ] \
|
||||
&& ! awk -F: -v u="$_uid" '$3==u {found=1; exit} END {exit !found}' "$_passwd_file" 2>/dev/null; then
|
||||
printf 'vllm:x:%s:%s:vllm:%s:/bin/bash\n' \
|
||||
"$_uid" "$(id -g)" "$HOME" >> "$_passwd_file"
|
||||
fi
|
||||
unset _uid _passwd_file
|
||||
|
||||
exec vllm serve "$@"
|
||||
Reference in New Issue
Block a user