#!/bin/bash # Prepare /media/thoughtframe/testsite like a server node and write site.sh for it. # Does not start the container — run upgrade.sh or start.sh after staging. set -euo pipefail ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" cd "$ROOT_DIR" SITE_ROOT="${SITE_ROOT:-/media/thoughtframe/testsite}" NODE="${NODE:-110}" UID_NOW="$(id -u)" GID_NOW="$(id -g)" # Host shape. Leave NETWORK_NAME empty to skip the fixed-IP overlay (e.g. when the # subnet is already taken on this host) and publish HTTP_PORT instead. NETWORK_NAME="${NETWORK_NAME-thoughtframe}" NETWORK_SUBNET="${NETWORK_SUBNET:-172.25.0.0/16}" HTTP_PORT="${HTTP_PORT-}" if [ -n "$NETWORK_NAME" ]; then IPV4_ADDRESS="${IPV4_ADDRESS:-172.25.0.${NODE}}" else IPV4_ADDRESS="" fi if [ -z "$NETWORK_NAME" ] && [ -z "$HTTP_PORT" ]; then echo "error: set HTTP_PORT when NETWORK_NAME is empty — the node would be unreachable" >&2 exit 2 fi ensure_site_dirs() { local root="$1" # Pre-create nested bind-mount targets as the deploy user. If Docker creates # webapp/WEB-INF/{data,elastic} itself, they become root-owned and bootstrap fails. if mkdir -p "${root}/webapp/WEB-INF/data" "${root}/webapp/WEB-INF/elastic" \ "${root}/data" "${root}/elastic" "${root}/services" "${root}/${NODE}" 2>/dev/null; then return 0 fi echo "Host cannot create ${root} — using docker helper as root..." docker run --rm \ -v /media:/media \ alpine sh -c "mkdir -p '${root}/webapp/WEB-INF/data' '${root}/webapp/WEB-INF/elastic' '${root}/data' '${root}/elastic' '${root}/services' '${root}/${NODE}' && chown -R ${UID_NOW}:${GID_NOW} '${root}'" } ensure_site_dirs "$SITE_ROOT" echo "Site dirs ready under ${SITE_ROOT}" CONTAINER_NAME="${CONTAINER_NAME:-testsite${NODE}}" SCRIPTROOT="${SITE_ROOT}/${NODE}" REGISTRY_DEFAULT="registry.thoughtframe.ai" # site.sh lives with the node scripts (ThoughtFrame-style) and is also copied to jetty-slim for monorepo helpers. write_site_sh() { local dest="$1" cat > "$dest" < "${SCRIPTROOT}/start.sh" < "${SCRIPTROOT}/stop.sh" < "${SCRIPTROOT}/restart.sh" < "${SCRIPTROOT}/logs.sh" < "${SCRIPTROOT}/bash.sh" < "${SCRIPTROOT}/create.sh" </dev/null 2>&1 || \\ docker network create --subnet "\${NETWORK_SUBNET:-172.25.0.0/16}" "\${NETWORK_NAME}" fi FULL_IMAGE="\${REGISTRY}/\${IMAGE}:\${TAG}" echo "Pulling \${FULL_IMAGE} ..." docker pull "\${FULL_IMAGE}" || docker pull "\${IMAGE}:\${TAG}" docker tag "\${FULL_IMAGE}" "\${IMAGE}:\${TAG}" 2>/dev/null || true COMPOSE=(docker compose -f compose.yml) if [ -n "\${NETWORK_NAME:-}" ]; then COMPOSE+=(-f compose.network.yml) fi if [ -n "\${HTTP_PORT:-}" ]; then COMPOSE+=(-f compose.ports.yml) fi docker rm -f "\${CONTAINER_NAME}" 2>/dev/null || true "\${COMPOSE[@]}" -p "\${COMPOSE_PROJECT_NAME}" up -d echo "Created \${CONTAINER_NAME}" if [ -n "\${IPV4_ADDRESS:-}" ]; then echo " http://\${IPV4_ADDRESS}:8080/ on network \${NETWORK_NAME}" fi if [ -n "\${HTTP_PORT:-}" ]; then echo " http://:\${HTTP_PORT}/" fi EOF cat > "${SCRIPTROOT}/upgrade.sh" </dev/null || true COMPOSE=(docker compose -f compose.yml) if [ -n "\${NETWORK_NAME:-}" ]; then COMPOSE+=(-f compose.network.yml) fi if [ -n "\${HTTP_PORT:-}" ]; then COMPOSE+=(-f compose.ports.yml) fi ./stop.sh || true docker rm -f "\${CONTAINER_NAME}" 2>/dev/null || true "\${COMPOSE[@]}" -p "\${COMPOSE_PROJECT_NAME}" up -d echo "Upgrade complete (\${IMAGE}:\${TAG})." # Each pull leaves the previous image untagged; on a root-disk site they fill the disk and push the # embedded OpenSearch past its flood-stage watermark (every index goes read-only). docker image prune -f || echo "WARNING: docker image prune failed; check free disk before the next upgrade" >&2 EOF chmod +x "${SCRIPTROOT}"/{start,stop,restart,logs,bash,create,upgrade}.sh echo "Node scripts in ${SCRIPTROOT}:" ls -1 "${SCRIPTROOT}"/*.sh echo echo "Next (fresh box):" echo " cd ${SCRIPTROOT} && ./create.sh" echo "Then day-to-day: ./start.sh ./stop.sh ./logs.sh ./upgrade.sh" if [ -n "${IPV4_ADDRESS}" ]; then echo "URL: http://${IPV4_ADDRESS}:8080/" fi if [ -n "${HTTP_PORT}" ]; then echo "URL: http://$(hostname):${HTTP_PORT}/" fi