mirror of
https://github.com/GMWalletApp/epusdt.git
synced 2026-07-07 18:26:16 +00:00
380 lines
13 KiB
Bash
Executable File
380 lines
13 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
readonly IMAGE_TAG="epctl-ubuntu-systemd-test:$(date +%s)-$$"
|
|
readonly CONTAINER_NAME="epctl-systemd-test-$$"
|
|
readonly BASE_IMAGE="ubuntu:24.04"
|
|
|
|
ACTIVE_LANG=""
|
|
POSITIONAL_ARGS=()
|
|
|
|
detect_default_language() {
|
|
local raw normalized
|
|
|
|
raw="${EPCTL_LANG:-}"
|
|
if [[ -z "${raw}" ]]; then
|
|
printf 'zh\n'
|
|
return
|
|
fi
|
|
|
|
normalized="${raw,,}"
|
|
case "${normalized}" in
|
|
zh|zh-*|zh_*|cn|chs|cht|中文)
|
|
printf 'zh\n'
|
|
;;
|
|
en|en-*|en_*|english)
|
|
printf 'en\n'
|
|
;;
|
|
*)
|
|
printf '%s\n' "${raw}"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
set_language() {
|
|
local requested normalized fallback_lang
|
|
|
|
requested="${1:-$(detect_default_language)}"
|
|
normalized="${requested,,}"
|
|
fallback_lang="${ACTIVE_LANG:-$(detect_default_language)}"
|
|
case "${normalized}" in
|
|
zh|zh-*|zh_*|cn|chs|cht|中文)
|
|
ACTIVE_LANG="zh"
|
|
;;
|
|
en|en-*|en_*|english)
|
|
ACTIVE_LANG="en"
|
|
;;
|
|
*)
|
|
ACTIVE_LANG="${fallback_lang}"
|
|
die "$(trf unsupported_language "${requested}")"
|
|
;;
|
|
esac
|
|
|
|
export EPCTL_LANG="${ACTIVE_LANG}"
|
|
}
|
|
|
|
trf() {
|
|
local key="$1"
|
|
shift || true
|
|
local template
|
|
|
|
case "${ACTIVE_LANG}:${key}" in
|
|
en:usage) template='Usage:' ;;
|
|
zh:usage) template='用法:' ;;
|
|
en:example) template='Example:' ;;
|
|
zh:example) template='示例:' ;;
|
|
en:missing_dependency) template='missing dependency: %s' ;;
|
|
zh:missing_dependency) template='缺少依赖:%s' ;;
|
|
en:arg_requires_value) template='%s requires a value' ;;
|
|
zh:arg_requires_value) template='%s 需要一个参数值' ;;
|
|
en:unsupported_language) template='unsupported language: %s' ;;
|
|
zh:unsupported_language) template='不支持的语言:%s' ;;
|
|
en:unsupported_arch) template='unsupported architecture: %s' ;;
|
|
zh:unsupported_arch) template='不支持的架构:%s' ;;
|
|
en:test_failed_collecting) template='test failed; collecting container diagnostics' ;;
|
|
zh:test_failed_collecting) template='测试失败,正在收集容器诊断信息' ;;
|
|
en:install_tag_invalid) template='install tag must be a real GitHub release tag such as v1.0.6' ;;
|
|
zh:install_tag_invalid) template='安装 tag 必须是真实的 GitHub release tag,例如 v1.0.6' ;;
|
|
en:upgrade_tag_invalid) template='upgrade tag must be a real GitHub release tag such as v1.0.6' ;;
|
|
zh:upgrade_tag_invalid) template='升级 tag 必须是真实的 GitHub release tag,例如 v1.0.6' ;;
|
|
en:base_image) template='base image: %s' ;;
|
|
zh:base_image) template='基础镜像:%s' ;;
|
|
en:building_image) template='building the Ubuntu systemd test image' ;;
|
|
zh:building_image) template='正在构建 Ubuntu systemd 测试镜像' ;;
|
|
en:starting_container) template='starting the test container' ;;
|
|
zh:starting_container) template='正在启动测试容器' ;;
|
|
en:systemd_not_ready) template='systemd did not become ready inside the container' ;;
|
|
zh:systemd_not_ready) template='容器内的 systemd 未能及时就绪' ;;
|
|
en:self_installing) template='installing epctl into PATH' ;;
|
|
zh:self_installing) template='正在将 epctl 安装到 PATH' ;;
|
|
en:downloading_release) template='downloading release %s' ;;
|
|
zh:downloading_release) template='正在下载 release %s' ;;
|
|
en:installing_release) template='installing epusdt from release %s' ;;
|
|
zh:installing_release) template='正在从 release %s 安装 epusdt' ;;
|
|
en:waiting_assets) template='waiting for epusdt to start and release www assets' ;;
|
|
zh:waiting_assets) template='正在等待 epusdt 启动并释放 www 静态文件' ;;
|
|
en:upgrading_release) template='upgrading epusdt from %s to %s' ;;
|
|
zh:upgrading_release) template='正在将 epusdt 从 %s 升级到 %s' ;;
|
|
en:checking_init_password) template='checking init-password behavior' ;;
|
|
zh:checking_init_password) template='正在检查 init-password 行为' ;;
|
|
en:expected_second_failure) template='expected init-password to fail after the password was changed' ;;
|
|
zh:expected_second_failure) template='密码修改后,init-password 本应失败,但它却成功了' ;;
|
|
en:verification_succeeded) template='docker verification succeeded for install=%s%s' ;;
|
|
zh:verification_succeeded) template='Docker 验证通过,install=%s%s' ;;
|
|
*)
|
|
template="missing translation: ${key}"
|
|
;;
|
|
esac
|
|
|
|
printf -- "${template}" "$@"
|
|
}
|
|
|
|
log() {
|
|
printf '[epctl-docker-test] %s\n' "$*" >&2
|
|
}
|
|
|
|
die() {
|
|
printf '[epctl-docker-test] error: %s\n' "$*" >&2
|
|
exit 1
|
|
}
|
|
|
|
usage() {
|
|
if [[ "${ACTIVE_LANG}" == "zh" ]]; then
|
|
cat <<'EOF'
|
|
用法:
|
|
./epctl-docker-test.sh [--lang zh|en] <install-tag> [upgrade-tag]
|
|
|
|
示例:
|
|
./epctl-docker-test.sh v1.0.6
|
|
./epctl-docker-test.sh --lang zh v1.0.6 v1.0.8
|
|
EOF
|
|
else
|
|
cat <<'EOF'
|
|
Usage:
|
|
./epctl-docker-test.sh [--lang zh|en] <install-tag> [upgrade-tag]
|
|
|
|
Example:
|
|
./epctl-docker-test.sh v1.0.6
|
|
./epctl-docker-test.sh --lang en v1.0.6 v1.0.8
|
|
EOF
|
|
fi
|
|
}
|
|
|
|
parse_global_options() {
|
|
POSITIONAL_ARGS=()
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--lang)
|
|
[[ $# -ge 2 ]] || die "$(trf arg_requires_value "--lang")"
|
|
set_language "$2"
|
|
shift 2
|
|
;;
|
|
--lang=*)
|
|
set_language "${1#*=}"
|
|
shift
|
|
;;
|
|
*)
|
|
POSITIONAL_ARGS+=("$1")
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
require_command() {
|
|
command -v "$1" >/dev/null 2>&1 || die "$(trf missing_dependency "$1")"
|
|
}
|
|
|
|
map_arch() {
|
|
case "$(uname -m)" in
|
|
x86_64|amd64)
|
|
printf 'amd64\n'
|
|
;;
|
|
aarch64|arm64)
|
|
printf 'arm64\n'
|
|
;;
|
|
*)
|
|
die "$(trf unsupported_arch "$(uname -m)")"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
docker_bash() {
|
|
docker exec "${CONTAINER_NAME}" bash -lc "$1"
|
|
}
|
|
|
|
cleanup() {
|
|
local exit_code="$?"
|
|
if [[ "${exit_code}" -ne 0 ]]; then
|
|
log "$(trf test_failed_collecting)"
|
|
docker exec "${CONTAINER_NAME}" bash -lc 'systemctl status epusdt --no-pager || true' || true
|
|
docker exec "${CONTAINER_NAME}" bash -lc 'journalctl -u epusdt --no-pager -n 100 || true' || true
|
|
fi
|
|
docker rm -f "${CONTAINER_NAME}" >/dev/null 2>&1 || true
|
|
docker rmi -f "${IMAGE_TAG}" >/dev/null 2>&1 || true
|
|
rm -rf "${TMPDIR_PATH:-}"
|
|
}
|
|
|
|
wait_for_systemd() {
|
|
local attempt
|
|
for attempt in $(seq 1 30); do
|
|
if docker exec "${CONTAINER_NAME}" bash -lc 'systemctl show --property=Version --value >/dev/null 2>&1'; then
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
return 1
|
|
}
|
|
|
|
set_language ""
|
|
parse_global_options "$@"
|
|
|
|
INSTALL_TAG="${POSITIONAL_ARGS[0]:-}"
|
|
UPGRADE_TAG="${POSITIONAL_ARGS[1]:-}"
|
|
[[ -n "${INSTALL_TAG}" ]] || {
|
|
usage
|
|
exit 1
|
|
}
|
|
[[ "${INSTALL_TAG}" == v* ]] || die "$(trf install_tag_invalid)"
|
|
if [[ -n "${UPGRADE_TAG}" && "${UPGRADE_TAG}" != v* ]]; then
|
|
die "$(trf upgrade_tag_invalid)"
|
|
fi
|
|
|
|
require_command docker
|
|
require_command curl
|
|
|
|
ASSET_ARCH="$(map_arch)"
|
|
ASSET_URL="https://github.com/GMWalletApp/epusdt/releases/download/${INSTALL_TAG}/epusdt-${INSTALL_TAG#v}-linux-${ASSET_ARCH}.tar.gz"
|
|
SUMS_URL="https://github.com/GMWalletApp/epusdt/releases/download/${INSTALL_TAG}/SHA256SUMS"
|
|
|
|
curl --fail --silent --show-error --head "${ASSET_URL}" >/dev/null
|
|
curl --fail --silent --show-error --head "${SUMS_URL}" >/dev/null
|
|
if [[ -n "${UPGRADE_TAG}" ]]; then
|
|
curl --fail --silent --show-error --head "https://github.com/GMWalletApp/epusdt/releases/download/${UPGRADE_TAG}/epusdt-${UPGRADE_TAG#v}-linux-${ASSET_ARCH}.tar.gz" >/dev/null
|
|
curl --fail --silent --show-error --head "https://github.com/GMWalletApp/epusdt/releases/download/${UPGRADE_TAG}/SHA256SUMS" >/dev/null
|
|
fi
|
|
|
|
TMPDIR_PATH="$(mktemp -d)"
|
|
trap cleanup EXIT
|
|
|
|
cat > "${TMPDIR_PATH}/Dockerfile" <<EOF
|
|
FROM ${BASE_IMAGE}
|
|
|
|
ENV container=docker
|
|
|
|
RUN apt-get update \
|
|
&& DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
|
ca-certificates \
|
|
curl \
|
|
jq \
|
|
sudo \
|
|
systemd \
|
|
systemd-sysv \
|
|
tar \
|
|
&& apt-get clean \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& useradd -m -s /bin/bash tester \
|
|
&& echo 'tester ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/tester \
|
|
&& chmod 0440 /etc/sudoers.d/tester
|
|
|
|
STOPSIGNAL SIGRTMIN+3
|
|
|
|
CMD ["/sbin/init"]
|
|
EOF
|
|
|
|
log "$(trf base_image "${BASE_IMAGE}")"
|
|
log "$(trf building_image)"
|
|
docker build -t "${IMAGE_TAG}" "${TMPDIR_PATH}" >/dev/null
|
|
|
|
log "$(trf starting_container)"
|
|
docker run -d \
|
|
--name "${CONTAINER_NAME}" \
|
|
--privileged \
|
|
--cgroupns=host \
|
|
-e container=docker \
|
|
--tmpfs /run \
|
|
--tmpfs /run/lock \
|
|
-v /sys/fs/cgroup:/sys/fs/cgroup:rw \
|
|
-v "${SCRIPT_DIR}:/work" \
|
|
-w /work \
|
|
"${IMAGE_TAG}" \
|
|
/sbin/init >/dev/null
|
|
|
|
wait_for_systemd || die "$(trf systemd_not_ready)"
|
|
|
|
log "$(trf self_installing)"
|
|
docker exec "${CONTAINER_NAME}" bash -lc "su - tester -c 'cd /work && ./epctl self-install'"
|
|
docker_bash '[[ -x /usr/local/bin/epctl ]]'
|
|
docker exec "${CONTAINER_NAME}" bash -lc 'su - tester -c "EPCTL_NO_COLOR=1 epctl help | grep -q \"^用法:\""'
|
|
docker exec "${CONTAINER_NAME}" bash -lc 'su - tester -c "EPCTL_NO_COLOR=1 epctl --lang zh help | grep -q \"^用法:\""'
|
|
docker exec "${CONTAINER_NAME}" bash -lc 'su - tester -c "EPCTL_NO_COLOR=1 epctl --lang en help | grep -q \"^Usage:\""'
|
|
|
|
log "$(trf downloading_release "${INSTALL_TAG}")"
|
|
docker exec "${CONTAINER_NAME}" env TAG="${INSTALL_TAG}" bash -lc 'su - tester -c "epctl download --tag ${TAG}"'
|
|
|
|
log "$(trf installing_release "${INSTALL_TAG}")"
|
|
docker exec "${CONTAINER_NAME}" env TAG="${INSTALL_TAG}" bash -lc 'su - tester -c "epctl install --tag ${TAG} --app-uri http://127.0.0.1:8000"'
|
|
|
|
log "$(trf waiting_assets)"
|
|
docker exec "${CONTAINER_NAME}" bash -lc '
|
|
for _ in $(seq 1 30); do
|
|
if systemctl is-active --quiet epusdt && [[ -f /opt/epusdt/www/index.html ]]; then
|
|
exit 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
systemctl status epusdt --no-pager || true
|
|
journalctl -u epusdt --no-pager -n 100 || true
|
|
exit 1
|
|
'
|
|
|
|
docker_bash '[[ -x /opt/epusdt/epusdt ]]'
|
|
docker_bash '[[ -f /opt/epusdt/.env ]]'
|
|
docker_bash 'systemctl is-active --quiet epusdt'
|
|
docker_bash '[[ -f /opt/epusdt/www/index.html ]]'
|
|
docker exec "${CONTAINER_NAME}" bash -lc 'su - tester -c "epctl show-config | grep -q \"^install=false$\""'
|
|
docker exec "${CONTAINER_NAME}" bash -lc 'su - tester -c "EPCTL_NO_COLOR=1 epctl --lang zh show-config | grep -q \"^install=false$\""'
|
|
docker exec "${CONTAINER_NAME}" bash -lc '
|
|
su - tester -c "epctl status >/tmp/epctl-status.out 2>/tmp/epctl-status.err"
|
|
! grep -q "systemd-journal" /tmp/epctl-status.err
|
|
'
|
|
docker exec "${CONTAINER_NAME}" bash -lc '
|
|
su - tester -c "epctl logs --lines 50 >/tmp/epctl-logs.out 2>/tmp/epctl-logs.err"
|
|
! grep -q "systemd-journal" /tmp/epctl-logs.err
|
|
'
|
|
|
|
if [[ -n "${UPGRADE_TAG}" ]]; then
|
|
log "$(trf upgrading_release "${INSTALL_TAG}" "${UPGRADE_TAG}")"
|
|
docker exec "${CONTAINER_NAME}" env TAG="${UPGRADE_TAG}" bash -lc 'su - tester -c "epctl upgrade --tag ${TAG}"'
|
|
docker exec "${CONTAINER_NAME}" bash -lc '
|
|
for _ in $(seq 1 30); do
|
|
if systemctl is-active --quiet epusdt && [[ -f /opt/epusdt/www/index.html ]]; then
|
|
exit 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
systemctl status epusdt --no-pager || true
|
|
journalctl -u epusdt --no-pager -n 100 || true
|
|
exit 1
|
|
'
|
|
docker exec "${CONTAINER_NAME}" bash -lc 'su - tester -c "epctl show-config | grep -q \"^install=false$\""'
|
|
docker exec "${CONTAINER_NAME}" bash -lc '
|
|
su - tester -c "epctl status >/tmp/epctl-status-upgrade.out 2>/tmp/epctl-status-upgrade.err"
|
|
! grep -q "systemd-journal" /tmp/epctl-status-upgrade.err
|
|
'
|
|
fi
|
|
|
|
log "$(trf checking_init_password)"
|
|
docker exec "${CONTAINER_NAME}" bash -lc '
|
|
response="$(su - tester -c "epctl init-password")"
|
|
password="$(printf "%s\n" "${response}" | jq -r ".data.password")"
|
|
printf "%s\n" "${response}" | jq -e ".status_code == 200 and (.data.password | type == \"string\" and length > 0)" >/dev/null
|
|
|
|
login_payload="$(jq -nc --arg username admin --arg password "${password}" "{username:\$username,password:\$password}")"
|
|
token="$(curl --fail --silent --show-error \
|
|
-H "Content-Type: application/json" \
|
|
-d "${login_payload}" \
|
|
http://127.0.0.1:8000/admin/api/v1/auth/login | jq -r ".data.token")"
|
|
[[ -n "${token}" && "${token}" != "null" ]]
|
|
|
|
change_payload="$(jq -nc --arg old_password "${password}" --arg new_password "new-pass-789" "{old_password:\$old_password,new_password:\$new_password}")"
|
|
curl --fail --silent --show-error \
|
|
-H "Authorization: Bearer ${token}" \
|
|
-H "Content-Type: application/json" \
|
|
-d "${change_payload}" \
|
|
http://127.0.0.1:8000/admin/api/v1/auth/password | jq -e ".status_code == 200" >/dev/null
|
|
|
|
failure_output="$(mktemp)"
|
|
if su - tester -c "epctl init-password" >"${failure_output}" 2>&1; then
|
|
cat "${failure_output}" >&2
|
|
rm -f "${failure_output}"
|
|
echo "'"$(trf expected_second_failure)"'" >&2
|
|
exit 1
|
|
fi
|
|
grep -Eq "\"status_code\"[[:space:]]*:[[:space:]]*10040" "${failure_output}"
|
|
rm -f "${failure_output}"
|
|
'
|
|
|
|
log "$(trf verification_succeeded "${INSTALL_TAG}" "${UPGRADE_TAG:+ upgrade=${UPGRADE_TAG}}")"
|