新增目标机已有 SpacetimeDB 与 otelcol-contrib 时复用本机安装的准备逻辑 补充 Prepare Provision Tools 传入 SPACETIME_ROOT,避免非默认路径检查错目录 新增 Server-Provision 工具准备回归检查脚本,防止已有工具时仍触发下载 更新开发运维文档与 Hermes 共享记忆,沉淀先检查目标机状态再准备文件的约定
340 lines
13 KiB
Bash
Executable File
340 lines
13 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
|
||
PROVISION_TOOLS_DIR="${PROVISION_TOOLS_DIR:-provision-tools}"
|
||
PROVISION_DOWNLOADS_DIR="${PROVISION_DOWNLOADS_DIR:-}"
|
||
OTELCOL_VERSION="${OTELCOL_VERSION:-0.151.0}"
|
||
PREPARE_OTELCOL="${PREPARE_OTELCOL:-${ENABLE_OTELCOL:-true}}"
|
||
OTELCOL_DOWNLOAD_ROOT="${OTELCOL_DOWNLOAD_ROOT:-https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download}"
|
||
OTELCOL_ARCHIVE_PATH="${OTELCOL_ARCHIVE_PATH:-}"
|
||
OTELCOL_TARGET_BIN="${OTELCOL_TARGET_BIN:-/usr/local/bin/otelcol-contrib}"
|
||
SPACETIME_INSTALLER_URL="${SPACETIME_INSTALLER_URL:-https://install.spacetimedb.com}"
|
||
SPACETIME_DOWNLOAD_ROOT="${SPACETIME_DOWNLOAD_ROOT:-https://github.com/clockworklabs/SpacetimeDB/releases/download/v2.4.1}"
|
||
SPACETIME_TARGET_HOST="${SPACETIME_TARGET_HOST:-x86_64-unknown-linux-gnu}"
|
||
SPACETIME_ROOT="${SPACETIME_ROOT:-/stdb}"
|
||
SPACETIME_EXPECTED_VERSION="${SPACETIME_EXPECTED_VERSION:-}"
|
||
SPACETIME_ARCHIVE_PATH="${SPACETIME_ARCHIVE_PATH:-}"
|
||
SPACETIME_INSTALLER_PATH="${SPACETIME_INSTALLER_PATH:-}"
|
||
SPACETIME_UPDATE_INSTALLER_PATH="${SPACETIME_UPDATE_INSTALLER_PATH:-}"
|
||
PROVISION_DOWNLOAD_PROXY="${PROVISION_DOWNLOAD_PROXY:-}"
|
||
PROVISION_NO_PROXY="${PROVISION_NO_PROXY:-127.0.0.1,localhost}"
|
||
PROVISION_REQUIRE_LOCAL_DOWNLOADS="${PROVISION_REQUIRE_LOCAL_DOWNLOADS:-false}"
|
||
PROVISION_TOOLS_TMP_PARENT="${PROVISION_TOOLS_TMP_PARENT:-${WORKSPACE:-$(pwd)}/.tmp/server-provision-tools}"
|
||
TMP_DIR_TO_CLEAN=""
|
||
OTELCOL_SOURCE_DESCRIPTION="skipped"
|
||
SPACETIME_SOURCE_DESCRIPTION="unset"
|
||
|
||
cleanup_tmp_dir() {
|
||
if [[ -n "${TMP_DIR_TO_CLEAN}" ]]; then
|
||
rm -rf "${TMP_DIR_TO_CLEAN}"
|
||
fi
|
||
}
|
||
|
||
require_cmd() {
|
||
local name="$1"
|
||
if ! command -v "${name}" >/dev/null 2>&1; then
|
||
echo "[prepare-provision-tools] 缺少命令: ${name}" >&2
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
configure_download_proxy() {
|
||
if [[ -z "${PROVISION_DOWNLOAD_PROXY}" ]]; then
|
||
return
|
||
fi
|
||
|
||
export HTTP_PROXY="${PROVISION_DOWNLOAD_PROXY}"
|
||
export HTTPS_PROXY="${PROVISION_DOWNLOAD_PROXY}"
|
||
export ALL_PROXY="${PROVISION_DOWNLOAD_PROXY}"
|
||
export http_proxy="${PROVISION_DOWNLOAD_PROXY}"
|
||
export https_proxy="${PROVISION_DOWNLOAD_PROXY}"
|
||
export all_proxy="${PROVISION_DOWNLOAD_PROXY}"
|
||
export NO_PROXY="${PROVISION_NO_PROXY}"
|
||
export no_proxy="${PROVISION_NO_PROXY}"
|
||
echo "[prepare-provision-tools] 已配置下载代理: ${PROVISION_DOWNLOAD_PROXY%%://*}://***"
|
||
}
|
||
|
||
download_file() {
|
||
local url="$1"
|
||
local output="$2"
|
||
|
||
if command -v curl >/dev/null 2>&1; then
|
||
curl -fsSL --retry 3 --retry-delay 2 "${url}" -o "${output}"
|
||
elif command -v wget >/dev/null 2>&1; then
|
||
wget -O "${output}" "${url}"
|
||
else
|
||
echo "[prepare-provision-tools] 需要 curl 或 wget 下载: ${url}" >&2
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
resolve_spacetime_expected_version() {
|
||
local download_root="${SPACETIME_DOWNLOAD_ROOT%/}"
|
||
|
||
if [[ -n "${SPACETIME_EXPECTED_VERSION}" ]]; then
|
||
printf "%s" "${SPACETIME_EXPECTED_VERSION}"
|
||
return
|
||
fi
|
||
|
||
if [[ "${download_root}" =~ /v([0-9]+(\.[0-9]+){1,2})$ ]]; then
|
||
printf "%s" "${BASH_REMATCH[1]}"
|
||
fi
|
||
}
|
||
|
||
target_otelcol_ready() {
|
||
local version_output
|
||
|
||
echo "[prepare-provision-tools] 检查目标机 otelcol-contrib: ${OTELCOL_TARGET_BIN}"
|
||
if [[ ! -x "${OTELCOL_TARGET_BIN}" ]]; then
|
||
echo "[prepare-provision-tools] 目标机 otelcol-contrib 不存在或不可执行,将准备交付文件。"
|
||
return 1
|
||
fi
|
||
|
||
version_output="$("${OTELCOL_TARGET_BIN}" --version 2>/dev/null || true)"
|
||
if [[ -n "${OTELCOL_VERSION}" && "${version_output}" != *"${OTELCOL_VERSION}"* ]]; then
|
||
echo "[prepare-provision-tools] 目标机 otelcol-contrib 版本不匹配,期望 ${OTELCOL_VERSION},当前: ${version_output:-unknown}"
|
||
return 1
|
||
fi
|
||
|
||
echo "[prepare-provision-tools] 目标机 otelcol-contrib 已满足要求: ${version_output:-version unknown}"
|
||
return 0
|
||
}
|
||
|
||
target_spacetime_ready() {
|
||
local target_cli="${SPACETIME_ROOT}/bin/current/spacetimedb-cli"
|
||
local target_standalone="${SPACETIME_ROOT}/bin/current/spacetimedb-standalone"
|
||
local expected_version version_output
|
||
|
||
echo "[prepare-provision-tools] 检查目标机 SpacetimeDB: ${SPACETIME_ROOT}/bin/current"
|
||
if [[ ! -x "${target_cli}" || ! -x "${target_standalone}" ]]; then
|
||
echo "[prepare-provision-tools] 目标机 SpacetimeDB current 目录不完整,将准备交付文件。"
|
||
return 1
|
||
fi
|
||
|
||
expected_version="$(resolve_spacetime_expected_version)"
|
||
version_output="$("${target_cli}" --version 2>/dev/null || true)"
|
||
if [[ -n "${expected_version}" && "${version_output}" != *"${expected_version}"* ]]; then
|
||
echo "[prepare-provision-tools] 目标机 SpacetimeDB 版本不匹配,期望 ${expected_version},当前: ${version_output:-unknown}"
|
||
return 1
|
||
fi
|
||
|
||
echo "[prepare-provision-tools] 目标机 SpacetimeDB 已满足要求: ${version_output:-version unknown}"
|
||
return 0
|
||
}
|
||
|
||
validate_relative_dir() {
|
||
local label="$1"
|
||
local path="$2"
|
||
|
||
if [[ -z "${path}" ]]; then
|
||
return
|
||
fi
|
||
if [[ "${path}" == /* || "${path}" == *..* || "${path}" == "." ]]; then
|
||
echo "[prepare-provision-tools] ${label} 只能是工作区内的相对路径: ${path}" >&2
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
make_spacetime_wrapper() {
|
||
local target="$1"
|
||
|
||
cat >"${target}" <<'EOF'
|
||
#!/usr/bin/env sh
|
||
set -eu
|
||
SELF_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||
exec "$SELF_DIR/bin/current/spacetimedb-cli" "$@"
|
||
EOF
|
||
chmod 0755 "${target}"
|
||
}
|
||
|
||
prepare_otelcol() {
|
||
local tmp_dir="$1"
|
||
local archive="${tmp_dir}/otelcol-contrib.tar.gz"
|
||
local extract_dir="${tmp_dir}/otelcol-contrib"
|
||
local url="${OTELCOL_DOWNLOAD_ROOT}/v${OTELCOL_VERSION}/otelcol-contrib_${OTELCOL_VERSION}_linux_amd64.tar.gz"
|
||
local downloaded_archive="${PROVISION_DOWNLOADS_DIR}/otelcol-contrib_${OTELCOL_VERSION}_linux_amd64.tar.gz"
|
||
local source_archive=""
|
||
local target="${PROVISION_TOOLS_DIR}/otelcol-contrib"
|
||
|
||
require_cmd tar
|
||
|
||
if target_otelcol_ready; then
|
||
echo "[prepare-provision-tools] 复用目标机已有 otelcol-contrib: ${OTELCOL_TARGET_BIN}"
|
||
install -m 0755 "${OTELCOL_TARGET_BIN}" "${target}"
|
||
"${target}" --version >/dev/null
|
||
OTELCOL_SOURCE_DESCRIPTION="target existing ${OTELCOL_TARGET_BIN}"
|
||
return
|
||
fi
|
||
|
||
if [[ -n "${OTELCOL_ARCHIVE_PATH}" && -f "${OTELCOL_ARCHIVE_PATH}" ]]; then
|
||
source_archive="${OTELCOL_ARCHIVE_PATH}"
|
||
elif [[ -n "${PROVISION_DOWNLOADS_DIR}" && -f "${downloaded_archive}" ]]; then
|
||
source_archive="${downloaded_archive}"
|
||
fi
|
||
if [[ "${PROVISION_REQUIRE_LOCAL_DOWNLOADS}" == "true" && -z "${source_archive}" ]]; then
|
||
echo "[prepare-provision-tools] 要求使用本地已有的 otelcol-contrib 来源,但目标机未满足且未找到下载包: ${downloaded_archive}" >&2
|
||
exit 1
|
||
fi
|
||
|
||
mkdir -p "${extract_dir}"
|
||
if [[ -n "${source_archive}" ]]; then
|
||
echo "[prepare-provision-tools] 使用已下载的 otelcol-contrib 包: ${source_archive}"
|
||
cp "${source_archive}" "${archive}"
|
||
OTELCOL_SOURCE_DESCRIPTION="local ${source_archive}"
|
||
else
|
||
echo "[prepare-provision-tools] 下载 otelcol-contrib: ${url}"
|
||
download_file "${url}" "${archive}"
|
||
OTELCOL_SOURCE_DESCRIPTION="download ${url}"
|
||
fi
|
||
tar -xzf "${archive}" -C "${extract_dir}"
|
||
|
||
if [[ ! -x "${extract_dir}/otelcol-contrib" ]]; then
|
||
echo "[prepare-provision-tools] otelcol-contrib 包中缺少可执行文件。" >&2
|
||
exit 1
|
||
fi
|
||
|
||
install -m 0755 "${extract_dir}/otelcol-contrib" "${target}"
|
||
"${target}" --version >/dev/null
|
||
}
|
||
|
||
prepare_spacetime() {
|
||
local tmp_dir="$1"
|
||
local install_root="${tmp_dir}/spacetime-root"
|
||
local target_dir="${PROVISION_TOOLS_DIR}/spacetime"
|
||
local archive_name="spacetime-${SPACETIME_TARGET_HOST}.tar.gz"
|
||
local downloaded_archive="${PROVISION_DOWNLOADS_DIR}/${archive_name}"
|
||
local source_archive=""
|
||
local update_name="spacetimedb-update-${SPACETIME_TARGET_HOST}"
|
||
local downloaded_update="${PROVISION_DOWNLOADS_DIR}/${update_name}"
|
||
local source_update=""
|
||
local prepared_update="${tmp_dir}/spacetimedb-update"
|
||
local downloaded_installer="${PROVISION_DOWNLOADS_DIR}/spacetime-install.sh"
|
||
local source_installer=""
|
||
|
||
if target_spacetime_ready; then
|
||
echo "[prepare-provision-tools] 复用目标机已有 SpacetimeDB 安装: ${SPACETIME_ROOT}/bin/current"
|
||
mkdir -p "${target_dir}"
|
||
cp -a "${SPACETIME_ROOT}/bin" "${target_dir}/bin"
|
||
chmod 0755 "${target_dir}/bin/current/spacetimedb-cli" "${target_dir}/bin/current/spacetimedb-standalone"
|
||
make_spacetime_wrapper "${target_dir}/spacetime"
|
||
"${target_dir}/spacetime" --version >/dev/null
|
||
SPACETIME_SOURCE_DESCRIPTION="target existing ${SPACETIME_ROOT}/bin/current"
|
||
return
|
||
fi
|
||
|
||
mkdir -p "${install_root}"
|
||
if [[ -n "${SPACETIME_ARCHIVE_PATH}" && -f "${SPACETIME_ARCHIVE_PATH}" ]]; then
|
||
source_archive="${SPACETIME_ARCHIVE_PATH}"
|
||
elif [[ -n "${PROVISION_DOWNLOADS_DIR}" && -f "${downloaded_archive}" ]]; then
|
||
source_archive="${downloaded_archive}"
|
||
fi
|
||
|
||
if [[ -n "${source_archive}" ]]; then
|
||
echo "[prepare-provision-tools] 使用已下载的 SpacetimeDB release tarball: ${source_archive}"
|
||
mkdir -p "${install_root}/bin/current"
|
||
tar -xzf "${source_archive}" -C "${install_root}/bin/current"
|
||
chmod 0755 "${install_root}/bin/current/spacetimedb-cli" "${install_root}/bin/current/spacetimedb-standalone"
|
||
SPACETIME_SOURCE_DESCRIPTION="local archive ${source_archive}"
|
||
elif [[ -n "${SPACETIME_UPDATE_INSTALLER_PATH}" && -f "${SPACETIME_UPDATE_INSTALLER_PATH}" ]]; then
|
||
source_update="${SPACETIME_UPDATE_INSTALLER_PATH}"
|
||
elif [[ -n "${PROVISION_DOWNLOADS_DIR}" && -f "${downloaded_update}" ]]; then
|
||
source_update="${downloaded_update}"
|
||
fi
|
||
if [[ "${PROVISION_REQUIRE_LOCAL_DOWNLOADS}" == "true" && -z "${source_archive}" ]]; then
|
||
echo "[prepare-provision-tools] 要求使用本地已有的 SpacetimeDB release tarball,但目标机未满足且未找到下载包: ${downloaded_archive}" >&2
|
||
exit 1
|
||
fi
|
||
|
||
if [[ -n "${source_archive}" ]]; then
|
||
:
|
||
elif [[ -n "${source_update}" ]]; then
|
||
echo "[prepare-provision-tools] 使用已下载的 SpacetimeDB Linux update installer: ${source_update}"
|
||
cp "${source_update}" "${prepared_update}"
|
||
chmod 0755 "${prepared_update}"
|
||
TMPDIR="${tmp_dir}" "${prepared_update}" --root-dir "${install_root}" -y
|
||
SPACETIME_SOURCE_DESCRIPTION="local update ${source_update}"
|
||
else
|
||
if [[ -n "${SPACETIME_INSTALLER_PATH}" && -f "${SPACETIME_INSTALLER_PATH}" ]]; then
|
||
source_installer="${SPACETIME_INSTALLER_PATH}"
|
||
elif [[ -n "${PROVISION_DOWNLOADS_DIR}" && -f "${downloaded_installer}" ]]; then
|
||
source_installer="${downloaded_installer}"
|
||
fi
|
||
|
||
if [[ "${PROVISION_REQUIRE_LOCAL_DOWNLOADS}" == "true" && -z "${source_installer}" ]]; then
|
||
echo "[prepare-provision-tools] 要求使用本地已有的 SpacetimeDB 官方安装器脚本,但未找到: ${downloaded_installer}" >&2
|
||
exit 1
|
||
elif [[ -n "${source_installer}" ]]; then
|
||
echo "[prepare-provision-tools] 使用已下载的 SpacetimeDB 官方安装器脚本: ${source_installer}"
|
||
cp "${source_installer}" "${tmp_dir}/spacetime-install.sh"
|
||
else
|
||
echo "[prepare-provision-tools] 下载 SpacetimeDB 官方安装器脚本: ${SPACETIME_INSTALLER_URL}"
|
||
download_file "${SPACETIME_INSTALLER_URL}" "${tmp_dir}/spacetime-install.sh"
|
||
fi
|
||
chmod 0755 "${tmp_dir}/spacetime-install.sh"
|
||
TMPDIR="${tmp_dir}" SPACETIME_DOWNLOAD_ROOT="${SPACETIME_DOWNLOAD_ROOT}" sh "${tmp_dir}/spacetime-install.sh" --root-dir "${install_root}" -y
|
||
SPACETIME_SOURCE_DESCRIPTION="installer ${SPACETIME_INSTALLER_URL}; download root ${SPACETIME_DOWNLOAD_ROOT}"
|
||
fi
|
||
|
||
if [[ ! -x "${install_root}/bin/current/spacetimedb-cli" ]]; then
|
||
echo "[prepare-provision-tools] SpacetimeDB 安装结果缺少 bin/current/spacetimedb-cli。" >&2
|
||
exit 1
|
||
fi
|
||
if [[ ! -x "${install_root}/bin/current/spacetimedb-standalone" ]]; then
|
||
echo "[prepare-provision-tools] SpacetimeDB 安装结果缺少 bin/current/spacetimedb-standalone。" >&2
|
||
exit 1
|
||
fi
|
||
|
||
mkdir -p "${target_dir}"
|
||
cp -a "${install_root}/bin" "${target_dir}/bin"
|
||
make_spacetime_wrapper "${target_dir}/spacetime"
|
||
|
||
"${target_dir}/spacetime" --version >/dev/null
|
||
}
|
||
|
||
main() {
|
||
local tmp_dir
|
||
|
||
require_cmd chmod
|
||
require_cmd cp
|
||
require_cmd install
|
||
require_cmd mktemp
|
||
require_cmd rm
|
||
require_cmd tar
|
||
|
||
validate_relative_dir "PROVISION_TOOLS_DIR" "${PROVISION_TOOLS_DIR}"
|
||
validate_relative_dir "PROVISION_DOWNLOADS_DIR" "${PROVISION_DOWNLOADS_DIR}"
|
||
if [[ -n "${PROVISION_DOWNLOADS_DIR}" && "${PROVISION_DOWNLOADS_DIR%/}" == "${PROVISION_TOOLS_DIR%/}" ]]; then
|
||
echo "[prepare-provision-tools] PROVISION_DOWNLOADS_DIR 不能等于 PROVISION_TOOLS_DIR,否则会被清理: ${PROVISION_DOWNLOADS_DIR}" >&2
|
||
exit 1
|
||
fi
|
||
|
||
configure_download_proxy
|
||
|
||
mkdir -p "${PROVISION_TOOLS_TMP_PARENT}"
|
||
tmp_dir="$(mktemp -d "${PROVISION_TOOLS_TMP_PARENT%/}/run.XXXXXX")"
|
||
TMP_DIR_TO_CLEAN="${tmp_dir}"
|
||
trap cleanup_tmp_dir EXIT
|
||
|
||
rm -rf "${PROVISION_TOOLS_DIR}"
|
||
mkdir -p "${PROVISION_TOOLS_DIR}"
|
||
|
||
if [[ "${PREPARE_OTELCOL}" == "true" ]]; then
|
||
prepare_otelcol "${tmp_dir}"
|
||
else
|
||
echo "[prepare-provision-tools] PREPARE_OTELCOL=${PREPARE_OTELCOL},跳过 otelcol-contrib 工具包准备。"
|
||
fi
|
||
prepare_spacetime "${tmp_dir}"
|
||
|
||
cat >"${PROVISION_TOOLS_DIR}/MANIFEST.txt" <<EOF
|
||
otelcol-contrib ${OTELCOL_VERSION} ${OTELCOL_SOURCE_DESCRIPTION}
|
||
spacetime ${SPACETIME_SOURCE_DESCRIPTION}
|
||
spacetime target host ${SPACETIME_TARGET_HOST}
|
||
require local downloads ${PROVISION_REQUIRE_LOCAL_DOWNLOADS}
|
||
EOF
|
||
|
||
echo "[prepare-provision-tools] 工具包已准备: ${PROVISION_TOOLS_DIR}"
|
||
find "${PROVISION_TOOLS_DIR}" -maxdepth 5 \( -type f -o -type l \) | sort
|
||
}
|
||
|
||
main "$@"
|