500 lines
13 KiB
Bash
Executable File
500 lines
13 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# --------------------------------------------------------- bash settings
|
|
#set -o xtrace
|
|
set -u
|
|
set -o pipefail
|
|
|
|
# sub ----------------------------------------------------- usage
|
|
usage () {
|
|
printf "Usage:\n"
|
|
printf " podmanctl [<options>] <server> [<server>...]\n"
|
|
printf " - command is assumed to be 'create'\n"
|
|
printf " podmanctl [${COMMANDS}] [<options>] <server> [<server>...]\n"
|
|
printf " Options:\n"
|
|
printf " For run|create:\n"
|
|
printf " -p (pull before run/create}\n"
|
|
printf " -e <additional_environment_vars>\n"
|
|
printf " For backup:\n"
|
|
printf " -d (keep containers down)\n"
|
|
printf " -w (store backups under weekday subdirectory)\n"
|
|
}
|
|
|
|
# --------------------------------------------------------- command
|
|
COMMANDS="ps|create|run|stop|start|restart|clean|backup|restore"
|
|
if [[ $# -lt 1 ]]; then
|
|
printf "Arguments missing\n"
|
|
usage
|
|
exit 2
|
|
fi
|
|
|
|
if [[ "$1" =~ ^(${COMMANDS})$ ]]; then
|
|
COMMAND="${1}"
|
|
shift
|
|
else
|
|
COMMAND="create"
|
|
fi
|
|
|
|
# --------------------------------------------------------- podman
|
|
export BUILDAH_FORMAT=docker
|
|
export BUILDAH_LAYERS=true
|
|
|
|
# --------------------------------------------------------- args
|
|
# ADD PULL OPTION
|
|
ARGS_ENV=""
|
|
PULL=""
|
|
DOWN="false"
|
|
WEEKDAY=""
|
|
while getopts ':pe:dw' OPT; do
|
|
case $OPT in
|
|
e)
|
|
if [[ "${COMMAND}" =~ ^(create|run)$ ]]; then
|
|
ARGS_ENV="${ARGS_ENV} --env ${OPTARG}"
|
|
else
|
|
printf "option 'e' does not apply to command '${COMMAND}'\n"
|
|
exit 1
|
|
fi
|
|
;;
|
|
p)
|
|
if [[ "${COMMAND}" =~ ^(create|run)$ ]]; then
|
|
PULL="--pull"
|
|
else
|
|
printf "option 'p' does not apply to command '${COMMAND}'\n"
|
|
exit 1
|
|
fi
|
|
;;
|
|
d)
|
|
if [[ "${COMMAND}" =~ ^(backup)$ ]]; then
|
|
DOWN="true"
|
|
else
|
|
printf "option 'd' does not apply to command '${COMMAND}'\n"
|
|
exit 1
|
|
fi
|
|
;;
|
|
w)
|
|
if [[ "${COMMAND}" =~ ^(backup)$ ]]; then
|
|
WEEKDAY="$(date +%^a)/"
|
|
else
|
|
printf "option 'w' does not apply to command '${COMMAND}'\n"
|
|
exit 1
|
|
fi
|
|
;;
|
|
|
|
\?)
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
shift $((OPTIND -1))
|
|
|
|
# arg checks
|
|
if [[ $# -lt 1 ]]; then
|
|
printf "Project name argument missing\n"
|
|
usage
|
|
exit 2
|
|
fi
|
|
|
|
# sub ----------------------------------------------------- error
|
|
error () {
|
|
printf "${1}\n"
|
|
exit 1
|
|
}
|
|
|
|
# sub ----------------------------------------------------- error - usage
|
|
erroru () {
|
|
printf "${1}\n"
|
|
exit 1
|
|
}
|
|
|
|
# sub ----------------------------------------------------- systemd - find
|
|
systemd-find () {
|
|
local SERVICE="$(${SYSTEMDCM} list-unit-files --all |grep -F "${1}")"
|
|
if [[ "x$SERVICE" =~ "x$1" ]]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
systemd-active () {
|
|
local ACTIVE="$(${SYSTEMDCM} is-active "${1}")"
|
|
if [[ $ACTIVE == "active" ]]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
#
|
|
sub ----------------------------------------------------- stop
|
|
stop-pod () {
|
|
local SERVICE="podman-${PODNAME}.service"
|
|
if systemd-active ${SERVICE}; then
|
|
if systemd-find ${SERVICE}; then
|
|
${SYSTEMDCM} stop ${SERVICE} || error " :: Stop Service"
|
|
fi
|
|
else
|
|
if podman pod exists "${PODNAME}"; then
|
|
podman pod stop -t 70 "${PODNAME}" || error " :: Stop Pod"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# sub ----------------------------------------------------- start
|
|
start-pod () {
|
|
local SERVICE="podman-${PODNAME}.service"
|
|
if systemd-find ${SERVICE}; then
|
|
${SYSTEMDCM} start ${SERVICE} || error " :: Start Service"
|
|
else
|
|
error ":: ${COMMAND} ${SERVER} - service not found!"
|
|
fi
|
|
}
|
|
|
|
# sub ----------------------------------------------------- restart
|
|
restart-pod () {
|
|
local SERVICE="podman-${PODNAME}.service"
|
|
if systemd-find ${SERVICE}; then
|
|
${SYSTEMDCM} restart ${SERVICE} || error " :: Restart Service"
|
|
else
|
|
error ":: ${COMMAND} ${SERVER} - service not found!"
|
|
fi
|
|
}
|
|
|
|
# sub ----------------------------------------------------- remove services
|
|
remove-services () {
|
|
local SERVICE="podman-${PODNAME}.service"
|
|
if [[ -f "${SYSTEMDIR}/${SERVICE}" ]]; then
|
|
${SYSTEMDCM} disable ${SERVICE} || error " :: Disable Service"
|
|
fi
|
|
find "${SYSTEMDIR}" \
|
|
-maxdepth 1 \
|
|
-name "podman-${SERVER}-*.service" \
|
|
-exec rm {} +
|
|
${SYSTEMDCM} daemon-reload
|
|
}
|
|
|
|
# sub ----------------------------------------------------- install services
|
|
# 4.4.0 onwards:
|
|
# --stop-timeout=70 \
|
|
# --restart-sec=10 \
|
|
install-services () {
|
|
(pushd "${SYSTEMDIR}" && \
|
|
podman generate systemd \
|
|
--name \
|
|
--new \
|
|
--pod-prefix=podman \
|
|
--container-prefix=podman \
|
|
--files ${PODNAME} && \
|
|
popd) && ${SYSTEMDCM} daemon-reload \
|
|
&& ${SYSTEMDCM} enable podman-${PODNAME}
|
|
}
|
|
|
|
# sub ----------------------------------------------------- clean
|
|
clean-pod () {
|
|
stop-pod
|
|
remove-services
|
|
}
|
|
|
|
# sub ----------------------------------------------------- backup volumes
|
|
backup-volumes () {
|
|
VOLUMES="$(podman volume ls --format='{{.Name}}' \
|
|
|grep -v ".backup" |grep "${SERVER}")"
|
|
if [[ -z $VOLUMES ]]; then
|
|
printf ":: ${COMMAND} ${SERVER} - no volumes found to backup.\n"
|
|
return
|
|
fi
|
|
for VOLUME in $VOLUMES; do
|
|
SOURCE="${VOLUME}"
|
|
TARGET="${VOLUME}.backup"
|
|
TARGET_VOLUME="$(docker volume ls -q --filter name="^${TARGET}$")"
|
|
if [[ -z $TARGET_VOLUME ]]; then
|
|
if docker volume create "${TARGET}"; then
|
|
printf \
|
|
":: ${COMMAND} ${SERVER} - target volume '${TARGET}' created\n"
|
|
else
|
|
printf \
|
|
":: ${COMMAND} ${SERVER} - target volume ${TARGET} could not be created\n"
|
|
return 1
|
|
fi
|
|
fi
|
|
printf ":: ${COMMAND} ${SERVER} - rsync ${SOURCE} to ${TARGET} commencing\n"
|
|
if podman run \
|
|
--rm \
|
|
--env "SOURCE_DIR=/source" \
|
|
--env "TARGET_DIR=/target" \
|
|
--volume "${SOURCE}:/source:ro" \
|
|
--volume "${TARGET}:/target" \
|
|
cor.cherished.me/system/helper-rsync \
|
|
> /dev/null; then
|
|
printf \
|
|
":: ${COMMAND} ${SERVER} - backup of ${VOLUME} successful\n"
|
|
else
|
|
printf \
|
|
":: ${COMMAND} ${SERVER} - error in backup of ${VOLUME}\n"; return 2
|
|
fi
|
|
done
|
|
}
|
|
|
|
# sub ----------------------------------------------------- save images
|
|
save-images () {
|
|
IMAGES="$(podman image ls --format="{{.Repository}}" \
|
|
|grep -v "docker.io" |grep "${SERVER}")"
|
|
if [[ -z $IMAGES ]]; then
|
|
printf ":: ${COMMAND} ${SERVER} - no images found\n"
|
|
return
|
|
fi
|
|
printf ":: ${COMMAND} ${SERVER} - SAVING IMAGES\n"
|
|
for IMAGE in $IMAGES; do
|
|
OUT="${BACKDIR}/${WEEKDAY}$(basename $IMAGE)"
|
|
printf ":: ${COMMAND} ${SERVER} - saving image ${IMAGE} to ${OUT}\n"
|
|
if podman image save \
|
|
--compress \
|
|
--format docker-dir \
|
|
--output "${OUT}" \
|
|
"${IMAGE}" \
|
|
> /dev/null; then
|
|
printf ":: ${COMMAND} ${SERVER} - saving ${IMAGE} successful\n"
|
|
else
|
|
printf ":: ${COMMAND} ${SERVER} - error saving ${IMAGE}\n"; return 2
|
|
fi
|
|
done
|
|
}
|
|
|
|
# --------------------------------------------------------- save backup volumes
|
|
save-backups () {
|
|
VOLUMES="$(podman volume ls --format='{{.Name}}' \
|
|
|grep ".backup" |grep "${SERVER}")"
|
|
if [[ -z $VOLUMES ]]; then
|
|
printf ":: ${COMMAND} ${SERVER} - no volumes found.\n"
|
|
return
|
|
fi
|
|
printf ":: ${COMMAND} ${SERVER} - EXPORT BACKUP VOLUMES.\n"
|
|
for VOLUME in $VOLUMES; do
|
|
OUT="${BACKDIR}/${WEEKDAY}${VOLUME}.tar"
|
|
printf ":: ${COMMAND} ${SERVER} - exporting volume ${VOLUME} to ${OUT}.\n" > /dev/null
|
|
if podman volume export \
|
|
--output "${OUT}" \
|
|
"${VOLUME}" > /dev/null; then
|
|
printf ":: ${COMMAND} ${SERVER} - exporting ${VOLUME} successful\n"
|
|
else
|
|
printf ":: ${COMMAND} ${SERVER} - error exporting ${VOLUME}\n"; return 2
|
|
fi
|
|
done
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# --------------------------------------------------------- system user
|
|
if [ "x$(id -u)x" = "x0x" ]; then
|
|
ROOT="true"
|
|
SYSTEMDIR="/etc/systemd/system"
|
|
SYSTEMDCM="systemctl"
|
|
[ -d $SYSTEMDIR ] || error "Missing /etc/systemd/system."
|
|
else
|
|
ROOT="false"
|
|
SYSTEMDIR="$HOME/.config/systemd/user"
|
|
SYSTEMDCM="systemctl --user"
|
|
mkdir -p "${SYSTEMDIR}" || error "Cannot create user systemd."
|
|
fi
|
|
|
|
|
|
# --------------------------------------------------------- system top
|
|
SYSTEMT="$(realpath $HOME/Server)"
|
|
[ -d $SYSTEMT ] || error "$HOME/Server directory missing\n"
|
|
|
|
# --------------------------------------------------------- system runtime
|
|
RUNTDIR="${SYSTEMT}/podman"
|
|
mkdir -p "${RUNTDIR}"
|
|
chmod 755 "${RUNTDIR}"
|
|
pushd "${RUNTDIR}" || exit 1
|
|
|
|
# --------------------------------------------------------- system dirs
|
|
BMNTDIR="${SYSTEMT}/.mount"
|
|
ENVRDIR="${SYSTEMT}/.envar"
|
|
BACKDIR="${SYSTEMT}/.store"
|
|
CERTDIR="${SYSTEMT}/.certs"
|
|
BUILDIR="${SYSTEMT}/builds"
|
|
|
|
# --------------------------------------------------------- other dirs
|
|
REPADDR="cor.cherished.me/cherished.me"
|
|
|
|
# --------------------------------------------------------- dir checks
|
|
for DIR in \
|
|
${BMNTDIR} ${ENVRDIR} ${BACKDIR} ${CERTDIR} ${BUILDIR}; do
|
|
mkdir -p "${DIR}"
|
|
chmod 700 "${DIR}"
|
|
done
|
|
|
|
# --------------------------------------------------------- rsync build
|
|
IMG="cor.cherished.me/system/helper-rsync"
|
|
if ! podman image exists ${IMG}; then
|
|
podman build \
|
|
-t "${IMG}" \
|
|
"${BUILDIR}/github/weikinhuang/rsync-backup"
|
|
fi
|
|
|
|
# --------------------------------------------------------- server loop
|
|
for SERVER in $*; do
|
|
|
|
if [[ ! -r ${SERVER} ]]; then
|
|
printf ":: Server spec file '${SERVER}' missing or not readable\n"
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
# --------------------------------------------------------- server vars
|
|
NETWORK="${SERVER}-net"
|
|
PODNAME="${SERVER}-pod"
|
|
|
|
printf ":: ${COMMAND} ${SERVER}\n"
|
|
|
|
# --------------------------------------------------------- network
|
|
podman network exists ${NETWORK} \
|
|
|| podman network create ${NETWORK}
|
|
SUBNET="$(podman network inspect ${NETWORK} \
|
|
|grep -w "\"subnet\":" |sed 's/[\":,a-z,A-Z ]//g')"
|
|
SUBNET_PREFIX="${SUBNET%\.0/24}"
|
|
#printf "${SUBNET_PREFIX}" > "${SERVER}-subnet"
|
|
|
|
# --------------------------------------------------------- proxy network
|
|
podman network exists proxy-net \
|
|
|| podman network create proxy-net
|
|
PROXY_SUBNET="$(podman network inspect proxy-net \
|
|
|grep -w "\"subnet\":" |sed 's/[\":,a-z,A-Z ]//g')"
|
|
PROXY_SUBNET_PREFIX="${PROXY_SUBNET%\.0/24}"
|
|
#printf "${PROXY_SUBNET_PREFIX}" > "proxy-subnet"
|
|
|
|
case $COMMAND in
|
|
|
|
(ps)
|
|
# --------------------------------------------------------- ps
|
|
podman ps -a --filter "pod=${PODNAME}" \
|
|
--format '{{.Names}}\t\t{{.Image}}\t\t{{.ExitCode}}\t\t{{.Status}}'
|
|
;;
|
|
|
|
(create|run)
|
|
# --------------------------------------------------------- create|run
|
|
SPEC_NAME="--name ${SERVER}"
|
|
SPEC_ENVFILE="--env-file ${ENVRDIR}"
|
|
SPEC_REPO="${REPADDR}/${SERVER}"
|
|
SPEC_CERTS="--volume ${CERTDIR}"
|
|
SPEC_VOLUME="--volume ${SERVER}"
|
|
SPEC_BMOUNT="--volume ${BMNTDIR}"
|
|
|
|
|
|
SPEC_IP="--ip ${SUBNET_PREFIX}"
|
|
SPEC_DNS="--dns ${SUBNET_PREFIX}.254"
|
|
SPEC_SUBNET="--subnet ${SUBNET}"
|
|
SPEC_NETWORK="--network ${NETWORK}"
|
|
SPEC_NETWORK_HOST="--network host"
|
|
SPEC_NETWORK_PREFIX="${SPEC_NETWORK}:ip=${SUBNET_PREFIX}"
|
|
SPEC_ALIAS="--network-alias "
|
|
SPEC_PUBLISH="--publish"
|
|
SPEC_PROXY_NET="--network proxy-net"
|
|
SPEC_PROXY_NET_PREFIX="${SPEC_PROXY_NET}:ip=${PROXY_SUBNET_PREFIX}"
|
|
|
|
SPEC_POD="--pod ${PODNAME}"
|
|
SPEC_ENV="--env SUBNET=${SUBNET} ${ARGS_ENV}"
|
|
SPEC_BUILD="podman build ${PULL}"
|
|
SPEC_LABELS="--label io.containers.autoupdate=registry"
|
|
SPEC_CREATE="podman create ${SPEC_POD} ${SPEC_LABELS} ${SPEC_ENV}"
|
|
|
|
SPEC_HOSTS=
|
|
|
|
# --------------------------------------------------------- recreate pod
|
|
SPEC_INFRA_NAME="--infra-name ${SERVER}-infra"
|
|
stop-pod
|
|
podman pod create \
|
|
${SPEC_NAME}-pod \
|
|
${SPEC_INFRA_NAME} \
|
|
--replace
|
|
|
|
# --------------------------------------------------------- create containers
|
|
source ${SERVER}
|
|
|
|
# --------------------------------------------------------- install services
|
|
install-services
|
|
|
|
# --------------------------------------------------------- run services
|
|
[[ $COMMAND = "run" ]] && $SYSTEMDCM start podman-${PODNAME}
|
|
|
|
printf ":: ${COMMAND} ${SERVER} - success\n"
|
|
;;
|
|
|
|
(stop)
|
|
|
|
if stop-pod; then
|
|
printf ":: ${COMMAND} ${SERVER} - success\n"
|
|
else
|
|
printf ":: ${COMMAND} ${SERVER} - failure\n"
|
|
fi
|
|
;;
|
|
|
|
(start)
|
|
|
|
if start-pod; then
|
|
printf ":: ${COMMAND} ${SERVER} - success\n"
|
|
else
|
|
printf ":: ${COMMAND} ${SERVER} - failure\n"
|
|
fi
|
|
sleep 1
|
|
;;
|
|
|
|
(restart)
|
|
|
|
if restart-pod; then
|
|
printf ":: ${COMMAND} ${SERVER} - success\n"
|
|
else
|
|
printf ":: ${COMMAND} ${SERVER} - failure\n"
|
|
fi
|
|
sleep 1
|
|
;;
|
|
|
|
(clean)
|
|
|
|
if clean-pod; then
|
|
printf ":: ${COMMAND} ${SERVER} - success\n"
|
|
else
|
|
printf ":: ${COMMAND} ${SERVER} - failure\n"
|
|
fi
|
|
;;
|
|
|
|
(backup)
|
|
|
|
save-images
|
|
if stop-pod && backup-volumes; then
|
|
if [[ $DOWN == "false" ]]; then
|
|
start-pod
|
|
fi
|
|
save-backups
|
|
printf ":: ${COMMAND} ${SERVER} - success\n"
|
|
else
|
|
printf ":: ${COMMAND} ${SERVER} - failure\n"
|
|
fi
|
|
;;
|
|
|
|
(restore)
|
|
|
|
printf ":: ${COMMAND} ${SERVER} - not implemented\n"; continue
|
|
;;
|
|
|
|
(?)
|
|
|
|
printf "Uknown command: $COMMAND\n"
|
|
|
|
esac
|
|
|
|
done
|
|
|
|
|
|
# if [[ ! "$(stat -L -c "%A" "${DIR}" |cut -c5-10 )" = "------" ]] then
|