podmanctl/podmanctl

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