myxce/all.sh
2026-08-21 15:43:15 +00:00

795 lines
26 KiB
Bash
Executable file
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# prepare env begin
# sudo chmod 775 /nix/var/nix/daemon-socket
# sudo systemctl restart nix-daemon
# prepare env end
# customzie begin
#g_code_base="/home/jsxing/allwork/git/xce"
g_code_base="/home/jsxing/allwork/git/myxce"
# services registry: name|binary path, managed by start/stop/restart/status
g_services=(
"controller|${g_code_base}/src/build/controller/xce_controller"
"scheduler|${g_code_base}/src/build/scheduler/xce_scheduler"
"worker|${g_code_base}/src/build/worker/xcecd"
"apiserver|${g_code_base}/src/build/frontend/apiserver/xce_apiserver"
)
# Unified deployment root: ALL configs, logs and runtime state live under one
# directory next to all.sh, so nothing references system dirs (/opt/xce) or
# stale repo paths. Layout:
# deploy/conf/ worker.yaml + other generated configs
# deploy/run/ pid files, certs, controller workdir, apiserver topdir
# deploy/logs/ controller/scheduler/worker log files
g_deploy_dir="${g_code_base}/deploy"
g_conf_dir="${g_deploy_dir}/conf" # generated config files (worker.yaml, ...)
g_run_dir="${g_deploy_dir}/run" # pid files, certs, controller/apiserver data
g_log_dir="${g_deploy_dir}/logs" # log files
# Log verbosity for ALL components. One of: info | debug | debug2 | debug3.
# controller/scheduler: mapped to -v / -vv / -vvv (argp --verbose)
# worker (xcecd): written into worker.yaml log_level field
# apiserver: written into logger.yaml level (zap; debug2/3 -> debug)
g_loglevel="debug3"
# Per-job worker binary install dir. xcecd_worker is exec'd by systemd AS the
# job's exec user (e.g. admin), so it must live somewhere every user can reach.
# The repo path under /home/<user> is usually 750 and blocks other users ->
# exec fails with "Permission denied" (sh exit 126) and every job dies instantly.
# /opt/xce/bin is the canonical XCE layout. svc_prepare_worker installs it there
# via sudo and svc_env_worker points XCE_SERVER_DIR at it. Can be overridden.
g_worker_bin_dir="/opt/xce/bin"
# customize end
# usage begin
# ./all.sh build -c # 清理
# ./all.sh build -d # 开发构建(带耗时统计)
# ./all.sh build -p # 生产构建(带耗时统计)
# ./all.sh config deploy # 构建+部署集群配置(cluster.bin)并重启 controller
# ./all.sh config backup # 备份所有服务配置到 ~/all_configs
# ./all.sh start [svc] # 启动服务(默认全部):controller scheduler worker apiserver
# ./all.sh stop [svc] # 停止服务(默认全部)
# ./all.sh restart [svc] # 重启服务(默认全部)
# ./all.sh status [svc] # 查看服务状态(默认全部)
# -d/-p:增量编译,只有 xceadmin 每次固定重跑(~11s)+ 首次/源码变更时全量。初次全量约 60s。
# -c:彻底,重建一次即可得到干净状态。
# svc 取值为:controller / scheduler / worker,可传多个,如 ./all.sh start controller scheduler
# worker (xcecd) 需要 root,通过 sudo 启动/停止;sudo 需免密或已有缓存凭证(sudo -n)
# 无 cluster.bin 时 controller 会拒绝 worker join(EINVAL)——用 ./all.sh config deploy 部署
# 所有配置/logs/run 统一在 ${g_deploy_dir}(deploy/)下:
# worker.yaml 生成在 ${g_conf_dir}/worker.yaml(不再用 /opt/xce/conf/worker.yaml)
# controller 配置在 ${g_run_dir}/controller/ctrl-1/conf
# apiserver 配置在 ${g_run_dir}/apiserver/config
# 日志在 ${g_log_dir}/,pid 在 ${g_run_dir}/
# usage end
# log in a function, please print the calling function name
function _in() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] [${FUNCNAME[1]}] $1"
}
# Map g_loglevel -> argp --verbose flag count (-v/-vv/-vvv) for controller and
# scheduler (they call verbose_to_loglevel(); see src/lib/utils.c).
function _verbose_flags() {
case "${g_loglevel:-info}" in
debug3) echo "-vvv" ;;
debug2) echo "-vv" ;;
debug) echo "-v" ;;
*) echo "" ;;
esac
}
# Map g_loglevel -> worker.yaml log_level value (xcecd level_to_int()).
function _worker_loglevel() {
case "${g_loglevel:-info}" in
debug|debug2|debug3|notice|warn|error|fatal|off) echo "${g_loglevel}" ;;
*) echo "info" ;;
esac
}
# Map g_loglevel -> apiserver logger.yaml level (zap; only debug/info/warn/...).
function _apiserver_loglevel() {
case "${g_loglevel:-info}" in
debug|debug2|debug3) echo "debug" ;;
*) echo "info" ;;
esac
}
# run a command and log the command and its output
function run_cmd() {
local datestr=$(date "+%Y-%m-%d %H:%M:%S")
local cmd="$1 2>&1"
echo "[${datestr}]executing: ${1}"
echo "${cmd}" | awk '
function cmd( c )
{
while( (c|getline foo) > 0 )
printf( "%s\n", foo )
close( c );
}
{
cmd( $0 );
}
'
}
# ---- service management begin ----
# Return the startup arguments for a service.
# Override these in the customize section to fit your deployment.
function svc_args_controller() {
echo "--myname ctrl-1 --peers ctrl-1=localhost:9990 \
--workdir ${g_run_dir}/controller --certdir ${g_run_dir}/certs \
--logfile ${g_log_dir}/controller.log --vcpus 4 $(_verbose_flags)"
}
function svc_args_scheduler() {
echo "-l ${g_log_dir}/scheduler.log $(_verbose_flags)"
}
function svc_args_worker() {
# NOTE: when -c is given xcecd IGNORES -l/-p/-m (config file wins), so the
# log dir is fully controlled by ${g_conf_dir}/worker.yaml (log_dir field).
echo "-c ${g_conf_dir}/worker.yaml -n localhost01"
}
# Extra environment for the worker (xcecd) process. XCE_SERVER_DIR tells xcecd
# where to find xcecd_worker for per-job workers; it must point at a directory
# every job exec user can reach (see g_worker_bin_dir). Without it xcecd infers
# the dir from its own binary path (under /home/<user>) which job users can't
# traverse -> exec fails "Permission denied" / exit 126. svc_start prepends
# these via `env` for sudo-launched services.
function svc_env_worker() {
echo "XCE_SERVER_DIR=${g_worker_bin_dir}"
}
function svc_args_apiserver() {
echo "-d ${g_run_dir}/apiserver"
}
# services that must run as root (launched/stopped via sudo)
function svc_needs_sudo() {
case "$1" in
worker) return 0 ;;
*) return 1 ;;
esac
}
# Optional per-service prepare hook, called by svc_start before launch.
# controller: ensure the self-signed TLS cert/key exist (like deploy-ha.sh).
function svc_prepare_controller() {
local certdir="${g_run_dir}/certs"
mkdir -p "${certdir}"
if [ ! -f "${certdir}/xce.crt" ] || [ ! -f "${certdir}/xce.key" ]; then
_in "Generating self-signed TLS cert/key in ${certdir}"
openssl req -x509 -newkey rsa:2048 -nodes -days 3650 \
-keyout "${certdir}/xce.key" -out "${certdir}/xce.crt" \
-subj "/CN=xce-dev-test" 2>/dev/null
fi
}
# apiserver: ensure <topdir>/config/app.yaml + logger.yaml exist
# (app.yaml schema-valid, backend -> controller on 9990). Without them the
# Go binary refuses to start.
function svc_prepare_apiserver() {
local topdir="${g_run_dir}/apiserver"
local confdir="${topdir}/config"
mkdir -p "${confdir}"
if [ ! -f "${confdir}/logger.yaml" ]; then
_in "Generating default ${confdir}/logger.yaml (level=$(_apiserver_loglevel))"
cat > "${confdir}/logger.yaml" <<EOF
logging:
level: "$(_apiserver_loglevel)"
encoding: "console"
outputPath: "apiserver.log"
file:
maxSize: 64
maxBackups: 10
maxAge: 30
compress: true
encoderConfig:
messageKey: "msg"
levelKey: "level"
timeKey: "ts"
nameKey: "logger"
callerKey: "caller"
functionKey: "func"
stacktraceKey: "stacktrace"
timeEncoder: "iso8601"
levelEncoder: "capital"
EOF
else
# keep existing logger.yaml but sync level to the configured g_loglevel
local want="$(_apiserver_loglevel)"
if ! grep -q '^ level: "'"${want}"'"' "${confdir}/logger.yaml"; then
_in "updating ${confdir}/logger.yaml level -> ${want}"
sed -i 's/^ level: ".*"/ level: "'"${want}"'"/' "${confdir}/logger.yaml"
fi
fi
if [ ! -f "${confdir}/rbac.yaml" ]; then
if [ -f "${g_code_base}/src/frontend/apiserver/config/rbac.yaml" ]; then
_in "Copying sample rbac.yaml"
cp "${g_code_base}/src/frontend/apiserver/config/rbac.yaml" \
"${confdir}/rbac.yaml"
else
_in "Generating minimal rbac.yaml"
cat > "${confdir}/rbac.yaml" <<'EOF'
permissions: []
roles: {}
users: {}
EOF
fi
fi
if [ ! -f "${confdir}/auth.yaml" ]; then
if [ -f "${g_code_base}/src/frontend/apiserver/config/auth.yaml" ]; then
_in "Copying sample auth.yaml"
cp "${g_code_base}/src/frontend/apiserver/config/auth.yaml" \
"${confdir}/auth.yaml"
else
_in "Generating minimal auth.yaml"
cat > "${confdir}/auth.yaml" <<'EOF'
provider: config_file
config_file:
users:
- username: admin
password: admin
roles: [cluster_admin]
EOF
fi
fi
if [ ! -f "${confdir}/app.yaml" ]; then
_in "Generating default ${confdir}/app.yaml"
cat > "${confdir}/app.yaml" <<'EOF'
app:
name: "xce_apiserver"
runmode: "release"
http_port: 8080
version: "1.0.0"
security:
https:
enabled: false
port: 8443
cert_file: "config/ssl/server.crt"
key_file: "config/ssl/server.key"
authentication:
token:
type: "jwt"
secretKey: "xce-api-key"
token_expire_time: 86400
rate_limit:
global:
rate: 1000
burst: 2000
window: second
endpoints:
/xce/v1/auth/logon:
rate: 100
burst: 200
window: second
whitelist:
- "0.0.0.0/0"
- "::/0"
file_upload:
max_size: 100 MB
allowed_types:
- "application/x-tar"
- "application/zip"
job:
max_array_size: 1000
cache:
refresh_interval: 30
cors:
enabled: true
allowed_origins:
- "*"
backend:
address: "localhost"
port: 9990
from: "xce_apiserver"
key: "keepsecret$"
EOF
fi
}
# worker: generate worker.yaml under ${g_conf_dir} so the worker never reads a
# system path (/opt/xce/conf/worker.yaml) or a stale repo's log_dir. The
# log_dir field must point at our ${g_log_dir}; when -c is given xcecd ignores
# -l so this file is the single source of truth for the worker's log location.
function svc_prepare_worker() {
mkdir -p "${g_conf_dir}"
local wf="${g_conf_dir}/worker.yaml"
local lvl="$(_worker_loglevel)"
if [ -f "$wf" ]; then
# keep existing file but sync log_level to the configured g_loglevel
if ! grep -q "log_level: \"${lvl}\"" "$wf"; then
_in "updating worker.yaml log_level -> ${lvl}"
sed -i "s/log_level:.*/log_level: \"${lvl}\"/" "$wf"
fi
_in "worker config: ${wf} (log_level=${lvl})"
else
_in "Generating ${wf}"
cat > "$wf" <<EOF
---
workerconf:
management_list: ["localhost"]
controller_port: 9990
log_level: "${lvl}"
log_dir: "${g_log_dir}"
EOF
fi
# Install xcecd_worker somewhere every job exec user can run it. The job's
# worker is launched by systemd with User=<exec user>; the repo path under
# /home/<user> is usually 750, so other users get "Permission denied"
# (exit 126) and jobs die instantly. xcecd finds the binary via
# XCE_SERVER_DIR (svc_env_worker), so it must match this install dir.
local worker_bin="${g_code_base}/src/build/worker/xcecd_worker"
local worker_dst="${g_worker_bin_dir}/xcecd_worker"
if [ -x "${worker_bin}" ]; then
if [ ! -f "${worker_dst}" ] || [ "${worker_bin}" -nt "${worker_dst}" ]; then
_in "installing ${worker_bin} -> ${worker_dst}"
sudo -n mkdir -p "${g_worker_bin_dir}"
sudo -n install -m 0755 "${worker_bin}" "${worker_dst}"
fi
else
_in "WARN: ${worker_bin} not found; jobs will fail to exec xcecd_worker" >&2
fi
}
# Optional per-service precheck hook, called by svc_start BEFORE launching.
# controller: refuse to start without a deployed cluster.bin — without one the
# controller never becomes primary and apiserver's backend init fails with
# -10013 (bug-1514). Prints the active config when present.
function svc_check_controller() {
local confdir="${g_run_dir}/controller/ctrl-1/conf"
local cur ref bin
if [ ! -f "${confdir}/current" ]; then
_in "controller 未部署集群配置(缺少 ${confdir}/current)" >&2
_in "请先运行: ./all.sh config deploy # 从 src/configs 生成 cluster.bin 并部署" >&2
_in "然后再: ./all.sh start" >&2
return 1
fi
ref="$(cat "${confdir}/current" 2>/dev/null)"
bin="${confdir}/${ref}"
if [ -n "$ref" ] && [ -f "$bin" ]; then
_in "controller 使用集群配置: ${bin} (current -> ${ref})"
return 0
fi
_in "controller 配置指针 current -> ${ref} 指向的文件不存在(${bin})" >&2
_in "请重新运行: ./all.sh config deploy" >&2
return 1
}
# list registered service names
function _svc_names() {
local entry n
for entry in "${g_services[@]}"; do
n="${entry%%|*}"
echo "$n"
done
}
# resolve binary path for a service name (empty if unknown)
function _svc_bin() {
local name="$1" entry n b
for entry in "${g_services[@]}"; do
n="${entry%%|*}"; b="${entry#*|}"
[ "$n" = "$name" ] && { echo "$b"; return 0; }
done
return 1
}
function _svc_pidfile() { echo "${g_run_dir}/$1.pid"; }
# check liveness of a service pid; uses sudo for root (sudo) services
function _svc_alive() {
local name="$1" pid="$2"
if svc_needs_sudo "$name"; then
sudo -n kill -0 "$pid" 2>/dev/null
else
kill -0 "$pid" 2>/dev/null
fi
}
# send a signal to a service pid; uses sudo for root (sudo) services
function _svc_kill() {
local name="$1" sig="$2" pid="$3"
if svc_needs_sudo "$name"; then
if [ -n "$sig" ]; then
sudo -n kill -"$sig" "$pid" 2>/dev/null
else
sudo -n kill "$pid" 2>/dev/null
fi
else
if [ -n "$sig" ]; then
kill -"$sig" "$pid" 2>/dev/null
else
kill "$pid" 2>/dev/null
fi
fi
}
# print the live pid of a service (or empty); cleanup stale pidfile
function _svc_pid() {
local name="$1" pidfile pid
pidfile="$(_svc_pidfile "$name")"
if [ -f "$pidfile" ]; then
pid="$(cat "$pidfile" 2>/dev/null)"
if [ -n "$pid" ] && _svc_alive "$name" "$pid"; then
echo "$pid"
return 0
fi
rm -f "$pidfile"
fi
return 1
}
# resolve target services from args; default to all registered
function _svc_resolve() {
if [ $# -eq 0 ]; then
_svc_names
return 0
fi
local name bin
for name in "$@"; do
bin="$(_svc_bin "$name")" || {
_in "unknown service: $name (valid: $(_svc_names | tr '\n' ' '))" >&2
return 1
}
echo "$name"
done
}
# start a single service (background, pidfile + log)
function svc_start() {
local name="$1" bin args pidfile log
bin="$(_svc_bin "$name")" || return 1
args="$(svc_args_${name} 2>/dev/null)" || args=""
[ -x "$bin" ] || { _in "binary not found: $bin (run ./all.sh build first)"; return 1; }
if [ -n "$(_svc_pid "$name")" ]; then
# already running: still report the active config (best-effort).
# stdout carries the "using config" line; stderr hints are muted here.
if declare -F "svc_check_${name}" >/dev/null 2>&1; then
"svc_check_${name}" 2>/dev/null || true
fi
_in "$name already running (pid $(_svc_pid "$name"))"
return 0
fi
mkdir -p "${g_run_dir}" "${g_log_dir}"
pidfile="$(_svc_pidfile "$name")"
log="${g_log_dir}/${name}.log"
# optional per-service prepare hook (e.g. controller TLS certs)
if declare -F "svc_prepare_${name}" >/dev/null 2>&1; then
"svc_prepare_${name}" || return 1
fi
# optional per-service precheck hook (e.g. controller requires cluster.bin)
if declare -F "svc_check_${name}" >/dev/null 2>&1; then
"svc_check_${name}" || return 1
fi
if svc_needs_sudo "$name"; then
_in "Starting ${name} as root via sudo: ${bin} ${args}"
# optional per-service env (e.g. worker: XCE_SERVER_DIR so job workers
# can exec xcecd_worker from a world-accessible dir)
local envp=""
if declare -F "svc_env_${name}" >/dev/null 2>&1; then
envp="$(svc_env_${name})"
fi
# inner bash writes its own pid, then exec's into the binary so the
# pidfile holds the real (root) process pid; nohup guards SIGHUP.
# sudo -n = non-interactive; requires NOPASSWD or a cached credential.
sudo -n env ${envp} bash -c "echo \$\$ > '${pidfile}'; exec nohup '${bin}' ${args}" \
>>"${log}" 2>&1 < /dev/null &
else
_in "Starting ${name}: ${bin} ${args}"
setsid nohup ${bin} ${args} >>"${log}" 2>&1 < /dev/null &
echo $! > "${pidfile}"
fi
disown 2>/dev/null || true
sleep 1
local pid
pid="$(_svc_pid "$name")"
if [ -n "$pid" ]; then
_in "${name} started (pid $pid, log ${log})"
else
_in "${name} FAILED to start -- check ${log}"
rm -f "${pidfile}"
return 1
fi
}
# stop a single service (pidfile, then pgrep fallback)
function svc_stop() {
local name="$1" bin pid pidfile
bin="$(_svc_bin "$name")" || return 1
pidfile="$(_svc_pidfile "$name")"
pid="$(_svc_pid "$name")"
if [ -z "$pid" ]; then
# fallback: match by binary name
pid="$(pgrep -f "$(basename "$bin")" 2>/dev/null | head -n1)"
fi
if [ -z "$pid" ]; then
_in "${name} is not running"
rm -f "${pidfile}"
return 0
fi
_in "Stopping ${name} (pid $pid)"
_svc_kill "$name" "" "$pid"
local i
for i in $(seq 1 10); do
_svc_alive "$name" "$pid" || break
sleep 0.3
done
if _svc_alive "$name" "$pid"; then
_in "${name} did not exit, sending SIGKILL"
_svc_kill "$name" 9 "$pid"
sleep 0.3
fi
rm -f "${pidfile}"
_in "${name} stopped"
}
# restart a single service
function svc_restart() {
local name="$1"
svc_stop "$name"
svc_start "$name"
}
# print status of a single service
function svc_status() {
local name="$1" bin pid pidfile log state
bin="$(_svc_bin "$name")" || return 1
pidfile="$(_svc_pidfile "$name")"
log="${g_log_dir}/${name}.log"
pid="$(_svc_pid "$name")"
if [ -n "$pid" ]; then
state="running"
else
state="stopped"
fi
printf "%-12s %-8s %-8s\n" "$name" "$state" "${pid:--}"
printf " binary: %s\n" "$bin"
if [ -f "$log" ]; then
printf " log: %s\n" "$log"
printf " last: %s\n" "$(tail -n1 "$log" 2>/dev/null)"
fi
}
# ---- service management end ----
# clean
function build_clean() {
_in "Cleaning build artifacts..."
local src_dir=${g_code_base}/src
for dir in build build-prod; do
if [ -d "${src_dir}/${dir}" ]; then
run_cmd "rm -rf ${src_dir}/${dir}"
_in "Removed ${dir}/"
fi
done
}
function build_dev() {
_in "Building development version..."
local src_dir=${g_code_base}/src
cd ${src_dir}
local start_ts=$(date +%s)
run_cmd "nix develop --extra-experimental-features \"nix-command flakes\" --command bash -c \"meson setup build --buildtype=debug -Db_sanitize=address && meson compile -C build\""
local end_ts=$(date +%s)
local elapsed=$((end_ts - start_ts))
_in "Build finished in $((elapsed / 60))m $((elapsed % 60))s (${elapsed}s total)"
echo "output is in ${src_dir}/build"
}
function build_prod() {
_in "Building production version..."
local src_dir=${g_code_base}/src
cd ${src_dir}
local start_ts=$(date +%s)
run_cmd "nix develop --extra-experimental-features \"nix-command flakes\" --command bash -c \"meson setup build-prod --buildtype=release && meson compile -C build-prod\""
local end_ts=$(date +%s)
local elapsed=$((end_ts - start_ts))
_in "Build finished in $((elapsed / 60))m $((elapsed % 60))s (${elapsed}s total)"
echo "output is in ${src_dir}/build-prod"
}
# deploy cluster config: build cluster.bin from src/configs via xceadmin
# (in nix develop), place it in ctrl-1/conf, set conf/current, then restart
# the controller so it loads the config. Without a deployed cluster.bin the
# controller rejects worker joins with EINVAL (no hostconfig matched).
function config_deploy() {
_in "Deploying cluster config..."
local src_dir=${g_code_base}/src
local xceadmin=""
local candidate
# xceadmin lives under whichever meson build dir; try each in turn
for candidate in \
"${src_dir}/build/xceadmin/.venv/bin/xceadmin" \
"${src_dir}/build-prod/xceadmin/.venv/bin/xceadmin" \
"$(command -v xceadmin 2>/dev/null || true)"
do
if [ -n "$candidate" ] && [ -x "$candidate" ]; then
xceadmin="$candidate"
break
fi
done
if [ -z "$xceadmin" ]; then
_in "xceadmin not found (run ./all.sh build first)"
return 1
fi
_in "Using xceadmin: ${xceadmin}"
local tmpbin="${g_run_dir}/.cluster.bin.tmp"
local confdir="${g_run_dir}/controller/ctrl-1/conf"
_in "Building cluster.bin from ${src_dir}/configs (in nix develop)"
cd ${src_dir}
run_cmd "nix develop --extra-experimental-features \"nix-command flakes\" --command bash -c \"${xceadmin} config build ${src_dir}/configs -o ${tmpbin}\""
if [ ! -s "${tmpbin}" ]; then
_in "cluster.bin build failed -- check output above"
rm -f "${tmpbin}"
return 1
fi
mkdir -p "${confdir}"
local tag
tag="$(sha256sum "${tmpbin}" | cut -c1-12)"
mv "${tmpbin}" "${confdir}/cluster.bin.${tag}"
echo "cluster.bin.${tag}" > "${confdir}/current"
_in "Deployed cluster.bin.${tag} (current -> cluster.bin.${tag}) in ${confdir}"
_in "Restarting controller to load config"
svc_restart controller
}
# back up all services' runtime configs (controller certs/conf, worker.yaml,
# apiserver configs) to ~/all_configs, preserving the directory structure.
function config_backup() {
_in "Backing up all service configs to ~/all_configs..."
local dest=~/all_configs
local n=0
# controller: TLS certs
if [ -d "${g_run_dir}/certs" ]; then
mkdir -p "${dest}/controller/certs"
cp -f ${g_run_dir}/certs/* "${dest}/controller/certs/" 2>/dev/null
_in "controller certs -> ${dest}/controller/certs/"
n=$((n + 1))
fi
# controller: cluster.bin + current
if [ -d "${g_run_dir}/controller/ctrl-1/conf" ]; then
mkdir -p "${dest}/controller/conf"
cp -f ${g_run_dir}/controller/ctrl-1/conf/cluster.bin.* \
"${g_run_dir}/controller/ctrl-1/conf/current" \
"${dest}/controller/conf/" 2>/dev/null
_in "controller conf -> ${dest}/controller/conf/"
n=$((n + 1))
fi
# worker: ${g_conf_dir}/worker.yaml (generated by svc_prepare_worker)
mkdir -p "${dest}/worker"
if [ -f "${g_conf_dir}/worker.yaml" ]; then
cp -f "${g_conf_dir}/worker.yaml" "${dest}/worker/"
_in "worker.yaml -> ${dest}/worker/"
n=$((n + 1))
else
_in "worker.yaml (${g_conf_dir}/worker.yaml) not found, skipped"
fi
# apiserver: 4 config files
if [ -d "${g_run_dir}/apiserver/config" ]; then
mkdir -p "${dest}/apiserver"
cp -f ${g_run_dir}/apiserver/config/*.yaml "${dest}/apiserver/" 2>/dev/null
_in "apiserver config -> ${dest}/apiserver/"
n=$((n + 1))
fi
# scheduler: no standalone config file
mkdir -p "${dest}/scheduler"
printf 'scheduler 无独立配置文件(启动参数 -l <logfile> 已由 all.sh svc_args_scheduler 管理)\n' \
> "${dest}/scheduler/README.txt"
_in "Backup complete (${n} groups) in ${dest}"
}
# main sub-command dispatch
function cmd_build() {
local mode="dev"
while getopts "cdp" opt; do
case $opt in
c) build_clean; exit 0 ;;
d) mode="dev" ;;
p) mode="prod" ;;
?) echo "Usage: $0 build [-c] [-d|-p]" && exit 1 ;;
esac
done
if [ "$mode" = "dev" ]; then
build_dev
else
build_prod
fi
}
function _cmd_svc_foreach() {
# $1 = action (start|stop|restart|status), rest = services
local action="$1"; shift
local targets rc=0
targets="$(_svc_resolve "$@")" || exit 1
local name
for name in $targets; do
if ! "svc_${action}" "$name"; then
rc=1
# fail-fast for start/restart: one bad service (e.g. controller
# without cluster.bin) should stop the whole launch so the user
# sees the precheck hint (./all.sh config deploy) immediately.
case "$action" in
start|restart) break ;;
esac
fi
done
return "$rc"
}
function cmd_start() { _cmd_svc_foreach start "$@"; }
function cmd_stop() { _cmd_svc_foreach stop "$@"; }
function cmd_restart() { _cmd_svc_foreach restart "$@"; }
function cmd_status() {
local targets
targets="$(_svc_resolve "$@")" || return 1
printf "%-12s %-8s %-8s\n" "SERVICE" "STATE" "PID"
local name
for name in $targets; do
svc_status "$name"
done
}
function cmd_config() {
case "${1:-deploy}" in
deploy) config_deploy ;;
backup) config_backup ;;
*) echo "Usage: $0 config {deploy|backup}" && exit 1 ;;
esac
}
# dispatch
case "${1:-}" in
build) shift; cmd_build "$@" ;;
config) shift; cmd_config "$@" ;;
start) shift; cmd_start "$@" ;;
stop) shift; cmd_stop "$@" ;;
restart) shift; cmd_restart "$@" ;;
status) shift; cmd_status "$@" ;;
*) echo "Usage: $0 {build|config|start|stop|restart|status} [args]" && exit 1 ;;
esac