Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,17 @@ JDMC artifact 的升级脚本完成数据、配置和 systemd 服务迁移。安
命令行使用 `jdmc` 作为 JDMC 服务目标。
`--skip-jdmc` 仅供 JDMC 发起 JumpServer 重启时避免停止自身,不是组件开关。

组件的目录、权限和密钥初始化统一由安装器负责:

```bash
./jmsctl.sh prepare chen video-worker
./jmsctl.sh prepare --containers jms_chen jms_video-worker
```

不指定服务时准备所有已启用服务。`--containers` 必须指定已停止的容器,按容器实际使用的
镜像和运行用户准备,适用于 JDMC 主备及组件补丁后的启动。此命令不启动或重建业务容器,
不改写配置或执行数据库操作。JDMC 在两端维护升级及拉起应用前调用它,继续负责主备启停。

## 离线镜像清单

`scripts/gists/image.sh` 是离线镜像选择和重标记规则的唯一来源。调用
Expand Down
25 changes: 17 additions & 8 deletions jmsctl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ function usage() {
echo " uninstall $(gettext 'Uninstall JumpServer')"
echo
echo "More Commands: "
echo " prepare [service ...] Prepare service files and permissions without starting services"
echo " prepare --containers <container ...> Prepare stopped containers using their installed images"
echo " load_image $(gettext 'Loading docker image')"
echo " backup_db $(gettext 'Backup database')"
echo " backup_no_audit $(gettext 'Backup database without audit data')"
Expand Down Expand Up @@ -103,9 +105,7 @@ function start() {
configure_jdmc || return 1
gen_safe_config >/dev/null
stop_disabled_video_worker || return 1
prepare_chen_volume || return 1
prepare_video_worker_volume || return 1
prepare_ansible_executor || return 1
prepare_services_for_start || return 1
EXE=$(get_docker_compose_cmd_line)
if [[ "$(get_config_or_env ANSIBLE_EXECUTOR_ENABLED)" != "0" ]]; then
${EXE} up -d --wait --wait-timeout 60 ansible-executor || return 1
Expand Down Expand Up @@ -173,9 +173,7 @@ function restart() {
echo -e "\n"

if [[ -n "${target}" && "${target}" != "ignore_db" ]]; then
if [[ "${target}" == "chen" || "${target}" == "jms_chen" ]]; then
prepare_chen_volume || return 1
fi
prepare_services_for_start "${target}" || return 1
${EXE} up -d "${target}" || return 1
remove_stopped_openbao_init_container
return
Expand Down Expand Up @@ -215,15 +213,15 @@ function video-worker() {
log_error "video-worker is disabled by VIDEO_WORKER_ENABLED=0"
return 1
fi
prepare_video_worker_volume && ${EXE} up -d
prepare_services_for_start video-worker && ${EXE} up -d
;;
stop) ${EXE} down -v ;;
restart)
if ! video_worker_can_start; then
log_error "video-worker is disabled by VIDEO_WORKER_ENABLED=0"
return 1
fi
${EXE} down -v && prepare_video_worker_volume && ${EXE} up -d
${EXE} down -v && prepare_services_for_start video-worker && ${EXE} up -d
;;
status) ${EXE} ps ;;
*)
Expand Down Expand Up @@ -255,6 +253,17 @@ function check_os() {
function main() {
check_os || return 3

# Preparation also runs on an HA standby. Do not render Compose, rewrite
# configuration, or enter any service/database lifecycle action here.
if [[ "${action}" == prepare ]]; then
[[ -f "${CONFIG_FILE}" ]] || {
log_error "Configuration file not found: ${CONFIG_FILE}"
return 3
}
prepare_services_for_start "${args[@]:1}"
return $?
fi

if [[ "${action}" == "help" || "${action}" == "h" || "${action}" == "-h" || "${action}" == "--help" ]]; then
echo ""
elif [[ "${action}" == "install" || "${action}" == "config" || "${action}" == "reconfig" || "${action}" == "uninstall" ]]; then
Expand Down
7 changes: 5 additions & 2 deletions scripts/gists/ansible-executor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ function prepare_ansible_executor() {
fi

credential_dir="${CONFIG_DIR}/ansible-executor"
tag=$(get_ansible_executor_tag) || return 1
image="$(get_image_namespace)/ansible-executor:${tag}"
image=${1:-}
if [[ -z "${image}" ]]; then
tag=$(get_ansible_executor_tag) || return 1
image="$(get_image_namespace)/ansible-executor:${tag}"
fi
mkdir -p "${credential_dir}" || return 1
chmod 700 "${credential_dir}" || return 1
# Use the shipped SSH tools; the host needs no additional key-generation package.
Expand Down
4 changes: 1 addition & 3 deletions scripts/gists/postgresql-upgrade-standalone.sh
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,10 @@ complete_internal_postgresql_upgrade() {
# Called only after application schema and JDMC upgrades have succeeded.
while read -r container running policy retries service; do
[[ "${running}" == true ]] || continue
if [[ "${service}" == chen ]]; then
prepare_chen_volume || return 1
fi
services+=("${service}")
done <"${PG18_WORK}/writers"
if (( ${#services[@]} )); then
prepare_services_for_start "${services[@]}" || return 1
cmd=$(get_docker_compose_cmd_line) || return 1
${cmd} up -d "${services[@]}" || return 1
fi
Expand Down
66 changes: 54 additions & 12 deletions scripts/gists/service.sh
Original file line number Diff line number Diff line change
Expand Up @@ -328,18 +328,57 @@ function stop_disabled_video_worker() {
fi
}

function prepare_services_for_start() {
local service enabled container image owner running metadata extra from_containers=0
if [[ "${1:-}" == --containers ]]; then
from_containers=1
shift
(( $# > 0 )) || {
log_error 'prepare --containers requires at least one container'
return 1
}
fi
local -a selected=("$@")
if (( $# == 0 )); then
enabled=$(get_enabled_services) || return 1
read -r -a selected <<<"${enabled}"
fi
for service in "${selected[@]}"; do
image='' owner=''
if [[ "${from_containers}" == 1 ]]; then
container=${service}
[[ "${container}" =~ ^/?jms_[A-Za-z0-9_.-]+$ ]] || return 1
metadata=$(docker inspect -f '{{.State.Running}}|{{.Image}}|{{index .Config.Labels "com.docker.compose.service"}}|{{.Config.User}}' "${container}") || return 1
IFS='|' read -r running image service owner extra <<<"${metadata}"
[[ "${running}" == false && "${image}" =~ ^sha256:[a-f0-9]{64}$ && \
"${service}" =~ ^[A-Za-z0-9_.-]+$ && -z "${extra}" ]] || {
log_error "Cannot prepare a running or invalid service container: ${container}"
return 1
}
fi
case "${service#jms_}" in
chen) prepare_chen_volume "${image}" "${owner}" || return 1 ;;
video-worker) prepare_video_worker_volume "${image}" "${owner}" || return 1 ;;
ansible-executor|ansible_executor) prepare_ansible_executor "${image}" || return 1 ;;
esac
done
}

function prepare_chen_volume() {
[[ "$(get_config_or_env CHEN_ENABLED)" != "0" ]] || return 0
local image owner data_dir folder
image="$(get_image_namespace)/chen:${VERSION}"
owner=$(docker image inspect -f '{{.Config.User}}' "${image}") || {
log_error "Unable to inspect Chen image user: ${image}"
return 1
}
image=${1:-"$(get_image_namespace)/chen:${VERSION}"}
owner=${2:-}
if [[ -z "${owner}" ]]; then
owner=$(docker image inspect -f '{{.Config.User}}' "${image}") || {
log_error "Unable to inspect Chen image user: ${image}"
return 1
}
fi
# Legacy Java images run as root and need no ownership migration.
case "${owner}" in ''|root|root:root|0|0:0) return 0 ;; esac
if [[ ! "${owner}" =~ ^[0-9]+:[0-9]+$ ]]; then
owner=$(docker run --rm --entrypoint /bin/sh "${image}" -c \
owner=$(docker run --rm --network none --user "${owner}" --entrypoint /bin/sh "${image}" -c \
'printf "%s:%s\n" "$(id -u)" "$(id -g)"') || return 1
fi
if [[ ! "${owner}" =~ ^[0-9]+:[0-9]+$ ]]; then
Expand Down Expand Up @@ -370,15 +409,18 @@ function prepare_video_worker_volume() {
data_dir="${volume_dir}/video-worker/data"
mkdir -p "${data_dir}" || return 1

image="$(get_image_namespace)/video-worker:${VERSION}"
user_spec=$(docker image inspect -f '{{.Config.User}}' "${image}") || {
log_error "Unable to inspect video-worker image user: ${image}"
return 1
}
image=${1:-"$(get_image_namespace)/video-worker:${VERSION}"}
user_spec=${2:-}
if [[ -z "${user_spec}" ]]; then
user_spec=$(docker image inspect -f '{{.Config.User}}' "${image}") || {
log_error "Unable to inspect video-worker image user: ${image}"
return 1
}
fi
if [[ "${user_spec}" =~ ^[0-9]+:[0-9]+$ ]]; then
owner=${user_spec}
else
owner=$(docker run --rm --entrypoint /bin/sh "${image}" -c \
owner=$(docker run --rm --network none --user "${user_spec:-0}" --entrypoint /bin/sh "${image}" -c \
'printf "%s:%s\n" "$(id -u)" "$(id -g)"') || {
log_error "Unable to resolve video-worker image user: ${image}"
return 1
Expand Down
Loading