blob: 265a26e04c9678a0c1ccfab2942d6619136ee30e [file]
#!/usr/bin/env 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.
# End-to-end regression tests for smoke-boot.sh. Each case launches a fake
# "service" through smoke-boot.sh and checks its verdict from the exit code --
# smoke-boot no longer scans logs, so its decision is driven purely by whether
# the process reaches LISTEN, exits, or hangs.
#
# Guards issue #6332: a service that boots fine but prints exception-name prose
# in its log (e.g. jOOQ's random "tip of the day") must PASS; and a service that
# crashes on boot must still FAIL.
set -uo pipefail
command -v python3 >/dev/null || { echo "python3 is required to run these tests" >&2; exit 1; }
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
smoke="$script_dir/smoke-boot.sh"
work="$(mktemp -d 2>/dev/null || mktemp -d -t smoke-boot)"
trap 'rm -rf "$work"' EXIT
rc=0
# Print a likely-free localhost TCP port.
free_port() {
python3 -c 'import socket; s=socket.socket(); s.bind(("127.0.0.1",0)); print(s.getsockname()[1]); s.close()'
}
pass() { echo "ok: $1"; }
failed() { echo "FAIL: $1"; rc=1; }
# --- #6332: a healthy service that logs the jOOQ tip must PASS ---
# It prints prose naming NoClassDefFoundError / ClassNotFoundException, then
# opens its port and stays up. The old log-scanning check flagged this as a
# crash; the process-based check must not.
port="$(free_port)"
cat >"$work/healthy" <<EOF
#!/usr/bin/env bash
echo "jOOQ tip of the day: A NoClassDefFoundError or ClassNotFoundException is often a sign of a version mismatch"
exec python3 -c "import socket, time; s = socket.socket(); s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1); s.bind(('127.0.0.1', $port)); s.listen(); time.sleep(30)"
EOF
chmod +x "$work/healthy"
if "$smoke" "$work/healthy" "$port" 15 >/dev/null 2>&1; then
pass "healthy boot that logs the jOOQ tip -> OK (#6332)"
else
failed "healthy boot that logs the jOOQ tip should be OK"
fi
# --- a service that crashes on boot (exits before listening) must FAIL ---
port="$(free_port)"
cat >"$work/crasher" <<EOF
#!/usr/bin/env bash
echo "startup failed" >&2
exit 1
EOF
chmod +x "$work/crasher"
if "$smoke" "$work/crasher" "$port" 15 >/dev/null 2>&1; then
failed "crash on boot should FAIL"
else
pass "crash on boot (exits before listening) -> FAIL"
fi
# --- a service that hangs without ever listening must FAIL (timeout) ---
port="$(free_port)"
cat >"$work/hang" <<EOF
#!/usr/bin/env bash
exec sleep 300
EOF
chmod +x "$work/hang"
if "$smoke" "$work/hang" "$port" 3 >/dev/null 2>&1; then
failed "hang without listening should FAIL"
else
pass "hang without listening -> FAIL (timeout)"
fi
# --- #6336: a crash must not be masked by a pre-existing listener on the port.
# Hold the port with an unrelated listener, then boot a crasher on it: the port
# is busy at launch, so smoke-boot must FAIL fast rather than mistake the
# squatter's LISTEN for a healthy boot. ---
port="$(free_port)"
python3 -c "import socket, time; s = socket.socket(); s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1); s.bind(('127.0.0.1', $port)); s.listen(); time.sleep(60)" &
squatter=$!
# Wait until the squatter is actually accepting connections before testing.
for _ in $(seq 1 50); do
python3 -c "import socket,sys; s=socket.socket(); s.settimeout(0.2); sys.exit(0 if s.connect_ex(('127.0.0.1',$port))==0 else 1)" && break
sleep 0.1
done
if "$smoke" "$work/crasher" "$port" 5 >/dev/null 2>&1; then
failed "a crash masked by a pre-existing listener should FAIL (#6336)"
else
pass "port already in use -> FAIL fast, crash not masked (#6336)"
fi
kill "$squatter" 2>/dev/null || true
wait "$squatter" 2>/dev/null || true
if [[ "$rc" -ne 0 ]]; then
echo "smoke-boot regression tests FAILED"
exit 1
fi
echo "smoke-boot regression tests passed"