One OLS container fronting many tenants' detached cac-lsphp sidecars — the
OLS analogue of shared-httpd. Runs NO PHP locally; every site's PHP goes to
its own sidecar over LSAPI (extProcessor type lsapi, address <sidecar>:9000).
Key design fact (established by PoC): OLS has NO top-level 'include' directive,
so render-shared-ols-config.sh assembles httpd_config.conf from the panel's
per-site files (vhconf.conf + site.meta) at boot and on every change — the
'include' OLS lacks. Per-site detail uses the OLS-native configFile +
vhost-scoped extprocessor model. LSCache is module-level (a configFile-loaded
vhost rejects a bare cache{} block); the WP LiteSpeed plugin controls
cacheability via X-LiteSpeed-Cache-Control headers.
- Dockerfile.shared-ols: litespeed base + inotify-tools/envsubst/openssl,
admin bound to loopback, :80/:443 self-signed, healthz HEALTHCHECK.
- entrypoint-shared-ols.sh: cert + health vhost + render + watcher, then
daemon-mode OLS supervision (reused from cac-litespeed so self-restarts
don't kill PID 1).
- render-shared-ols-config.sh: strip stock (incl local lsphp) + append base +
per-site stanzas + listeners with all maps + catch-all health vhost.
- ols-htaccess-watcher.sh: inotify debounce+floor -> lswsctrl restart (spec 5.3).
- configs/shared-ols/{httpd_config_base,vhconf}.tpl.
- CI: Build-Shared-OLS job.
Verified locally end-to-end: zero-site boot healthy on :443; add site via the
panel contract -> Host-routed to the right sidecar (SAPI=litespeed); real
client IP + HTTPS behind X-Forwarded headers; LSCache miss->hit; .htaccess
change triggers graceful restart; unknown Host hits health catch-all (200).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
59 lines
2.3 KiB
Bash
59 lines
2.3 KiB
Bash
#!/usr/bin/env bash
|
|
## ols-htaccess-watcher.sh — graceful-restart the shared OLS when any tenant's
|
|
## .htaccess changes. OLS reads .htaccess (RewriteFile) at (re)start, NOT per
|
|
## request, so without this a WordPress permalink/LiteSpeed-Cache change would
|
|
## silently not take effect. Required by spec 5.3.
|
|
##
|
|
## Watches all docroots for .htaccess writes, COALESCES bursts (a WP plugin save
|
|
## touches the file several times) within a debounce window, and RATE-LIMITS to
|
|
## a floor (one restart per FLOOR seconds) so many tenants saving at once can't
|
|
## trigger a restart storm. Debounce/floor are env-tunable (panel discloses the
|
|
## resulting "~60s" window to customers).
|
|
##
|
|
## Failure of THIS process is the silent-ticket failure mode (spec 7): if it
|
|
## dies, tenants' rewrite changes stop applying with no error. The entrypoint
|
|
## runs it and the panel monitors it (check-ols-htaccess-watcher.php).
|
|
set -uo pipefail
|
|
|
|
WATCH_ROOT="${OLS_WATCH_ROOT:-/mnt/users}"
|
|
DEBOUNCE="${OLS_HTACCESS_DEBOUNCE:-15}" # coalesce window (s)
|
|
FLOOR="${OLS_HTACCESS_FLOOR:-60}" # min seconds between restarts
|
|
LSWSCTRL=/usr/local/lsws/bin/lswsctrl
|
|
last_restart=0
|
|
|
|
log() { echo "ols-htaccess-watcher: $*" >&2; }
|
|
|
|
do_restart() {
|
|
now=$(date +%s)
|
|
if [ $((now - last_restart)) -lt "$FLOOR" ]; then
|
|
log "within ${FLOOR}s floor — coalescing, skipping restart"
|
|
return
|
|
fi
|
|
if "$LSWSCTRL" restart >/dev/null 2>&1; then
|
|
last_restart=$now
|
|
log "graceful restart issued (.htaccess change)"
|
|
else
|
|
log "WARNING: lswsctrl restart failed"
|
|
fi
|
|
}
|
|
|
|
if ! command -v inotifywait >/dev/null 2>&1; then
|
|
log "FATAL: inotifywait not installed (inotify-tools)"; exit 1
|
|
fi
|
|
mkdir -p "$WATCH_ROOT"
|
|
log "watching $WATCH_ROOT for .htaccess changes (debounce=${DEBOUNCE}s floor=${FLOOR}s)"
|
|
|
|
## -m monitor, -r recursive. We filter to .htaccess in the read loop rather than
|
|
## --include so this works on older inotify-tools too. modify/create/delete/move
|
|
## all matter (delete of .htaccess also changes rewrite behavior).
|
|
inotifywait -m -r -e modify,create,delete,move "$WATCH_ROOT" --format '%f' 2>/dev/null |
|
|
while read -r fname; do
|
|
case "$fname" in
|
|
.htaccess) ;;
|
|
*) continue ;;
|
|
esac
|
|
## Drain further events for DEBOUNCE seconds (coalesce the burst), then act.
|
|
while read -r -t "$DEBOUNCE" _; do :; done
|
|
do_restart
|
|
done
|