mirror of
https://github.com/alexbelgium/hassio-addons.git
synced 2026-08-25 06:13:32 +02:00
Reported: on upgrade from an existing 7.17.9 install, the add-on failed to start with "mv: cannot move '/data/config' to '/data/config.bak-7.17.9': Permission denied". Root cause: a previous fix in this same release restored `USER 1000:0` at the end of the Dockerfile to match the upstream base image's own final USER directive. But the upstream 8.19 entrypoint no longer drops privileges itself (confirmed: it execs elasticsearch directly, no gosu/chroot dance), and existing installs have /data owned by root (7.17.9's default image variant runs fully as root). A non-root container can never chown or move that data. Revert to root at runtime, matching how this add-on always ran and matching its own AppArmor profile (chown, setuid, setgid, sys_chroot, mount capabilities — all meaningless for a non-root process anyway). Root stays required for the build-time entrypoint patch too, unchanged from the prior fix. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
144 lines
6.3 KiB
Bash
144 lines
6.3 KiB
Bash
#!/bin/bash
|
|
# shellcheck shell=bash
|
|
# Sourced by /usr/local/bin/docker-entrypoint.sh (right after "set -e"),
|
|
# before Elasticsearch starts. Runs as root (the image stays root at
|
|
# runtime - see Dockerfile); the official entrypoint does not drop
|
|
# privileges itself, and Elasticsearch ends up running as root too.
|
|
#
|
|
# Responsibilities:
|
|
# 1. Export user env_vars from /data/options.json
|
|
# 2. Default xpack.security.enabled=false (7.x behavior) unless user overrides
|
|
# 3. Relocate data & config to /data for persistence (idempotent)
|
|
# 4. Guard major-version data migrations (7.x -> 8.x is automatic)
|
|
|
|
echo "-----------------------------------------------------------"
|
|
echo " Add-on: Elasticsearch server"
|
|
echo " Upstream version: ${UPSTREAM_VERSION:-unknown}"
|
|
echo "-----------------------------------------------------------"
|
|
|
|
ES_HOME="/usr/share/elasticsearch"
|
|
PERSISTENT_HOME="/data"
|
|
VERSION_MARKER="$PERSISTENT_HOME/.addon-upstream-version"
|
|
OPTIONS_JSON="/data/options.json"
|
|
|
|
############################
|
|
# 1 Export user env_vars #
|
|
############################
|
|
|
|
if [ -f "$OPTIONS_JSON" ] && command -v jq >/dev/null 2>&1; then
|
|
while IFS= read -r pair; do
|
|
name=$(jq -r '.name // empty' <<<"$pair")
|
|
value=$(jq -r '.value // empty' <<<"$pair")
|
|
if [[ $name =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then
|
|
echo "Setting env variable from options: $name"
|
|
export "$name"="$value"
|
|
elif [ -n "$name" ]; then
|
|
echo "WARNING: ignoring invalid env_vars name: $name"
|
|
fi
|
|
done < <(jq -c '.env_vars[]?' "$OPTIONS_JSON" 2>/dev/null || true)
|
|
fi
|
|
|
|
##################################
|
|
# 2 Security default (7.x parity)#
|
|
##################################
|
|
|
|
# ES 8+ enables security + TLS by default, which breaks plain-http clients
|
|
# such as the homeassistant-elasticsearch component. Keep the previous 7.x
|
|
# behavior unless the user explicitly configures xpack.security themselves
|
|
# (either as a dotted setting or via the ES_SETTING_* translation).
|
|
if ! env | grep -qiE '^(xpack\.security\.|ES_SETTING_XPACK_SECURITY_)'; then
|
|
export ES_SETTING_XPACK_SECURITY_ENABLED=false
|
|
echo "Security: xpack.security.enabled=false (default; override by setting ES_SETTING_XPACK_SECURITY_ENABLED in env_vars)"
|
|
fi
|
|
|
|
############################
|
|
# 3 Migration guard #
|
|
############################
|
|
|
|
current_version="${UPSTREAM_VERSION:-0.0.0}"
|
|
current_major="${current_version%%.*}"
|
|
data_version=""
|
|
|
|
if [ -f "$VERSION_MARKER" ]; then
|
|
data_version="$(head -n 1 "$VERSION_MARKER" | tr -cd '0-9.')"
|
|
elif [ -d "$PERSISTENT_HOME/data" ] && [ -n "$(ls -A "$PERSISTENT_HOME/data" 2>/dev/null)" ]; then
|
|
# Existing data without a marker: only 7.17.9 was ever shipped before markers
|
|
data_version="7.17.9"
|
|
fi
|
|
|
|
if [ -n "$data_version" ] && [[ $current_major =~ ^[0-9]+$ ]]; then
|
|
data_major="${data_version%%.*}"
|
|
if [ "$data_major" -gt "$current_major" ]; then
|
|
echo "FATAL: existing data was written by Elasticsearch $data_version but this add-on runs $current_version."
|
|
echo "Downgrading Elasticsearch data is not supported. Restore a Home Assistant snapshot taken with the newer version, or delete the add-on data to start fresh."
|
|
exit 1
|
|
elif [ "$((current_major - data_major))" -gt 1 ]; then
|
|
echo "FATAL: existing data was written by Elasticsearch $data_version, which is more than one major version behind $current_version."
|
|
echo "Elasticsearch can only upgrade data from the previous major version. Upgrade stepwise (e.g. $data_major.x -> $((data_major + 1)).x -> ...) or delete the add-on data to start fresh."
|
|
exit 1
|
|
elif [ "$data_major" -lt "$current_major" ]; then
|
|
echo "NOTICE: one-time automatic data migration from Elasticsearch $data_version to $current_version."
|
|
echo "NOTICE: indices are upgraded automatically on startup. This can take a while on large datasets - do NOT stop the add-on during the first start."
|
|
# The bundled config from the old major is stale (jvm.options, log4j2,
|
|
# security settings). Archive it so a fresh one is seeded below.
|
|
if [ -d "$PERSISTENT_HOME/config" ] && [ ! -L "$PERSISTENT_HOME/config" ]; then
|
|
config_backup="$PERSISTENT_HOME/config.bak-$data_version"
|
|
if [ ! -e "$config_backup" ]; then
|
|
mv "$PERSISTENT_HOME/config" "$config_backup"
|
|
echo "NOTICE: previous config archived to $config_backup. Re-apply any custom settings to the new config."
|
|
fi
|
|
fi
|
|
# The container config dir may still symlink to the archived config
|
|
if [ -L "$ES_HOME/config" ]; then
|
|
rm -f "$ES_HOME/config"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
############################
|
|
# 4 Data persistence #
|
|
############################
|
|
|
|
mkdir -p "$PERSISTENT_HOME"
|
|
for dir in "data" "config"; do
|
|
if [ ! -L "$ES_HOME/$dir" ]; then
|
|
if [ -d "$ES_HOME/$dir" ]; then
|
|
cp -rn "$ES_HOME/$dir" "$PERSISTENT_HOME" 2>/dev/null || true
|
|
rm -rf "${ES_HOME:?}/$dir"
|
|
fi
|
|
mkdir -p "$PERSISTENT_HOME/$dir"
|
|
ln -s "$PERSISTENT_HOME/$dir" "$ES_HOME/$dir"
|
|
fi
|
|
done
|
|
|
|
# Make the persisted files usable by the elasticsearch user (uid 1000),
|
|
# which the official entrypoint drops to when started as root
|
|
if [ "$(id -u)" -eq 0 ]; then
|
|
chown -R 1000:0 "$PERSISTENT_HOME/data" "$PERSISTENT_HOME/config" 2>/dev/null || true
|
|
fi
|
|
|
|
echo "Data location: $PERSISTENT_HOME (persistent). Please wait while elasticsearch starts..."
|
|
|
|
############################
|
|
# 5 Record data version #
|
|
############################
|
|
|
|
# Only record the running version once ES is confirmed healthy, so a failed
|
|
# upgrade attempt never masks the true on-disk data lineage
|
|
if [ "$data_version" != "$current_version" ]; then
|
|
(
|
|
for _ in $(seq 1 180); do
|
|
# Check the HTTP status directly instead of curl -f: a 401 means
|
|
# Elasticsearch is up and answering (security just requires
|
|
# auth), so it must count as healthy too, not as a failure.
|
|
status=$(curl -A "HealthCheck: Docker/1.0" -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:9200" 2>/dev/null || true)
|
|
if [ "$status" = "200" ] || [ "$status" = "401" ]; then
|
|
echo "$current_version" >"$VERSION_MARKER"
|
|
echo "Elasticsearch $current_version started successfully; data version recorded."
|
|
exit 0
|
|
fi
|
|
sleep 10
|
|
done
|
|
) &
|
|
fi
|