Compare commits
3 commits
deployment
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| ba0a0155c8 | |||
| 0cf5c4bc02 | |||
| d9d0136d70 |
1 changed files with 81 additions and 7 deletions
88
panelctl.sh
88
panelctl.sh
|
|
@ -17,6 +17,7 @@ PANEL_GROUP="${PANEL_GROUP:-panelroutes}"
|
|||
# as KEY=VALUE words (passed to compose via env(1), never sourced).
|
||||
CURRENT_APP=""
|
||||
COMPOSE_ARGS=()
|
||||
ORPHAN_ARGS=()
|
||||
APP_ENV_ARGS=()
|
||||
APP_ENV_KEYS=()
|
||||
|
||||
|
|
@ -198,6 +199,7 @@ load_app() {
|
|||
local manifest
|
||||
manifest="$(app_manifest "${name}")"
|
||||
[[ -f "${manifest}" ]] || fail "app '${name}' does not exist"
|
||||
unset APP_COMPOSE_PROJECT
|
||||
# shellcheck disable=SC1090
|
||||
source "${manifest}"
|
||||
|
||||
|
|
@ -235,10 +237,20 @@ app_ports_override() {
|
|||
echo "${STACKS_DIR}/$1/.panel-ports.yaml"
|
||||
}
|
||||
|
||||
# The compose project the app's containers belong to: the app's name, unless
|
||||
# the manifest keeps an older one (see adopt_legacy_project).
|
||||
compose_project() {
|
||||
echo "${APP_COMPOSE_PROJECT:-${CURRENT_APP}}"
|
||||
}
|
||||
|
||||
# The app's compose file followed by whichever generated overrides exist.
|
||||
refresh_compose_args() {
|
||||
local override
|
||||
COMPOSE_ARGS=(-f "${APP_COMPOSE_FILE}")
|
||||
COMPOSE_ARGS=(-p "$(compose_project)" -f "${APP_COMPOSE_FILE}")
|
||||
# Other git apps may share a kept legacy project; their containers would
|
||||
# look like orphans.
|
||||
ORPHAN_ARGS=(--remove-orphans)
|
||||
[[ "$(compose_project)" == "${CURRENT_APP}" ]] || ORPHAN_ARGS=()
|
||||
for override in "$(app_ports_override "${CURRENT_APP}")" "$(app_env_override "${CURRENT_APP}")"; do
|
||||
if [[ -f "${override}" ]]; then
|
||||
COMPOSE_ARGS+=(-f "${override}")
|
||||
|
|
@ -597,6 +609,56 @@ cmd_render_route() {
|
|||
log info "rendered route ${route_file}"
|
||||
}
|
||||
|
||||
# Before panelctl passed -p, compose named the project after the compose
|
||||
# file's directory, so every git app (repo/compose.yaml) shared the project
|
||||
# "repo". Move the app's old containers out of the way so they are recreated
|
||||
# under its own name. Named volumes are prefixed with the project name, so an
|
||||
# app whose containers use them keeps its old project instead.
|
||||
adopt_legacy_project() {
|
||||
[[ -z "${APP_COMPOSE_PROJECT:-}" ]] || return 0
|
||||
local dir id project legacy="" volumes
|
||||
local ids=()
|
||||
dir="$(cd "$(dirname "${APP_COMPOSE_FILE}")" 2>/dev/null && pwd -P)" || return 0
|
||||
|
||||
while IFS='|' read -r id project; do
|
||||
[[ -n "${id}" && -n "${project}" && "${project}" != "${CURRENT_APP}" ]] || continue
|
||||
ids+=("${id}")
|
||||
legacy="${project}"
|
||||
done < <(run_podman ps --all --filter "label=com.docker.compose.project.working_dir=${dir}" \
|
||||
--format '{{.ID}}|{{index .Labels "com.docker.compose.project"}}' 2>/dev/null || true)
|
||||
[[ ${#ids[@]} -gt 0 ]] || return 0
|
||||
|
||||
volumes="$(run_podman inspect --format '{{range .Mounts}}{{if eq .Type "volume"}}{{.Name}} {{end}}{{end}}' "${ids[@]}" 2>/dev/null | xargs || true)"
|
||||
if [[ -n "${volumes}" ]]; then
|
||||
echo "APP_COMPOSE_PROJECT=\"${legacy}\"" >>"$(app_manifest "${CURRENT_APP}")"
|
||||
APP_COMPOSE_PROJECT="${legacy}"
|
||||
refresh_compose_args
|
||||
echo "Keeping compose project '${legacy}', because the app's containers use named volumes (${volumes})"
|
||||
log warning "app '${CURRENT_APP}' keeps compose project '${legacy}' because its containers use named volumes"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Moving the app's containers from compose project '${legacy}' to '${CURRENT_APP}'"
|
||||
run_podman rm --force "${ids[@]}" >/dev/null || fail "could not remove the old containers"
|
||||
# The shared pod goes away once no other app's containers are left in it.
|
||||
run_podman pod rm "pod_${legacy}" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
# True when a container of the app runs an older image than its tag now
|
||||
# points at, e.g. after a rebuild. podman-compose only recreates containers
|
||||
# when the compose file itself changes, so without this a push that only
|
||||
# changes code would build a new image and keep running the old one.
|
||||
containers_outdated() {
|
||||
local id image current
|
||||
while IFS='|' read -r id image; do
|
||||
[[ -n "${id}" && -n "${image}" ]] || continue
|
||||
current="$(run_podman image inspect --format '{{.Id}}' "${image}" 2>/dev/null)" || continue
|
||||
[[ "${current}" == "${id}"* || "${id}" == "${current}"* ]] || return 0
|
||||
done < <(run_podman ps --all --filter "label=com.docker.compose.project=$(compose_project)" \
|
||||
--format '{{.ImageID}}|{{.Image}}' 2>/dev/null || true)
|
||||
return 1
|
||||
}
|
||||
|
||||
cmd_deploy() {
|
||||
local name="$1"
|
||||
validate_name "${name}"
|
||||
|
|
@ -607,10 +669,21 @@ cmd_deploy() {
|
|||
cmd_render_route "${name}"
|
||||
prepare_ports_override "${name}"
|
||||
prepare_env_override "${name}"
|
||||
adopt_legacy_project
|
||||
|
||||
# Stream compose output as it happens (the panel shows it live in the
|
||||
# deployment log) and keep a copy in the journal.
|
||||
if ! run_compose "${COMPOSE_ARGS[@]}" up -d --build --remove-orphans 2>&1 | tee >(journal_copy); then
|
||||
if ! run_compose "${COMPOSE_ARGS[@]}" build 2>&1 | tee >(journal_copy); then
|
||||
log err "Deployment failed for app '${name}'"
|
||||
fail "image build failed"
|
||||
fi
|
||||
|
||||
local up_args=(up -d --no-build "${ORPHAN_ARGS[@]}")
|
||||
if containers_outdated; then
|
||||
echo "Images changed, recreating the app's containers"
|
||||
up_args+=(--force-recreate)
|
||||
fi
|
||||
if ! run_compose "${COMPOSE_ARGS[@]}" "${up_args[@]}" 2>&1 | tee >(journal_copy); then
|
||||
log err "Deployment failed for app '${name}'"
|
||||
fail "compose up failed"
|
||||
fi
|
||||
|
|
@ -625,11 +698,12 @@ cmd_restart() {
|
|||
|
||||
log info "Restarting app '${name}'"
|
||||
|
||||
run_compose "${COMPOSE_ARGS[@]}" down --remove-orphans || fail "compose down failed"
|
||||
adopt_legacy_project
|
||||
run_compose "${COMPOSE_ARGS[@]}" down "${ORPHAN_ARGS[@]}" || fail "compose down failed"
|
||||
prepare_ports_override "${name}"
|
||||
prepare_env_override "${name}"
|
||||
|
||||
if ! run_compose "${COMPOSE_ARGS[@]}" up -d --build --remove-orphans 2>&1; then
|
||||
if ! run_compose "${COMPOSE_ARGS[@]}" up -d --build "${ORPHAN_ARGS[@]}" 2>&1; then
|
||||
fail "compose up failed during restart"
|
||||
fi
|
||||
|
||||
|
|
@ -641,7 +715,7 @@ cmd_stop() {
|
|||
validate_name "${name}"
|
||||
load_app "${name}"
|
||||
|
||||
run_compose "${COMPOSE_ARGS[@]}" down --remove-orphans || fail "compose down failed"
|
||||
run_compose "${COMPOSE_ARGS[@]}" down "${ORPHAN_ARGS[@]}" || fail "compose down failed"
|
||||
log info "stopped app '${name}'"
|
||||
}
|
||||
|
||||
|
|
@ -742,7 +816,7 @@ cmd_remove() {
|
|||
validate_name "${name}"
|
||||
load_app "${name}"
|
||||
|
||||
run_compose "${COMPOSE_ARGS[@]}" down --remove-orphans 2>/dev/null || true
|
||||
run_compose "${COMPOSE_ARGS[@]}" down "${ORPHAN_ARGS[@]}" 2>/dev/null || true
|
||||
|
||||
# Remove this app's block from the aggregate routes file.
|
||||
local route_file
|
||||
|
|
@ -878,7 +952,7 @@ cmd_volume_clear() {
|
|||
load_app "${name}"
|
||||
|
||||
log info "clearing volume data for app '${name}'"
|
||||
run_compose "${COMPOSE_ARGS[@]}" down --remove-orphans 2>/dev/null || true
|
||||
run_compose "${COMPOSE_ARGS[@]}" down "${ORPHAN_ARGS[@]}" 2>/dev/null || true
|
||||
|
||||
local data_dir="${APP_VOLUME_DIR}/data"
|
||||
if [[ -d "${data_dir}" ]]; then
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue