my-runtipi/_bin/rtpctl.d

368 lines
13 KiB
Bash
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#! /usr/bin/env bash
RPH_UID=${RPH_UID:-${UID}}
[ "${SUDO}" == "sudo" ] || SUDO=
# Identify source path (even if symlinked)
SOURCE=${BASH_SOURCE[0]}
while [ -L "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )
SOURCE=$(readlink "$SOURCE")
[[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
SCRIPT_DIR=$( cd -- "$( dirname -- "${SOURCE}" )" &> /dev/null && pwd )
BASE_NAME="$(basename -- "$0")"
BASE_BASE_NAME="${BASE_NAME%.*}"
RUNTIPI_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
RUNTIPI_CLI="${RUNTIPI_ROOT}/runtipi-cli"
set -e
cd "${RUNTIPI_ROOT}" > /dev/null
. ./user-config/.env.local
runtipi-cli() {
if ! [ -x "${RUNTIPI_CLI}" ]; then
>&2 printf '%s\n' \
"ERROR: ${RUNTIPI_CLI} not found or not executable!"
return 1
fi
"${RUNTIPI_CLI}" "${@}"
}
append_file_param() {
[ -e "${2:?File name missing}" ] || return
echo "${*}"
}
sort-tipi() {
# 111 is followed an NBSP literal
# the 111 lines will be added only if `runtipi` is found
# sort --unique makes sure only one such line exists
# This in effect creates a methodical spacing between runtipi
# and the rest of the list
sed -Ee 's|^|555|; s|^555runtipi|111 \n000runtipi|;' \
| sort --unique \
| sed -Ee 's/^[[:digit:]]{3}//'
}
dls() {
local base='{{.Status}}\t{{.ID}}\t{{.Names}}\t{{.Image}}' #'\t{{.Networks}}\t{{.Ports}}\t{{.Mounts}}'
local compose='{{.Label "com.docker.compose.project"}}\t{{.Label "com.docker.compose.service"}}'
local format="table $compose\t$base"
${SUDO} docker container ls --all --format "$format" | (sed -u '1s/.*/\U&/; q'; sort-tipi)
}
get-docker-list() {
local POST_PROCESS=" | sed -Ee '#PP1 #PP2 #PP3' #PP_SORT"
local PP1='s/@.+//g;'
local PP2='s/ ix-/\nix-/g; s/(^|\n)ix-/\1/g;'
local PP3=''
local PP_SORT='| sort-tipi'
local SRC="" SRC_DOCKER="${SUDO} docker ps -a --format '{{.Label \"com.docker.compose.project\"}}@{{.Names}}' | sed -Ee 's/^@/<_no-compose-project_>:/;'"
local FILTER=""
local LIST=''
local MK_SEARCH_LIST="| xargs -r | sed 's/@//g; s/^/^(/; s/ *$/)@/; s/ /|/g;'"
local ARG=${1:---help}
while [ -n "$ARG" ]; do
case "$ARG" in
--debug)
set -x
;;
-G)
POST_PROCESS="${MK_SEARCH_LIST}"
PP_SORT=''
;;
-a)
SRC="${SRC_DOCKER}"
if [ -z "$LIST" ]; then
ARG="+X"
continue
fi
;;
-c)
PP1=''
if [ -z "$SRC" ]; then
ARG="-r"
continue
fi
;;
-r)
SRC="${SRC_DOCKER/ps -a /ps }"
if [ -z "$LIST" ]; then
ARG="+X"
continue
fi
;;
+X)
PP2=''
;;
ix|truenas)
LIST="${SUDO} find /mnt/.ix-apps/app_configs/* -maxdepth 0 -printf 'ix-%f@ '"
;;
tp|runtipi)
LIST="${SUDO} find apps/* -maxdepth 0 -printf '%f@ ' ; echo runtipi"
;;
other)
FILTER="grep -vE \""$(get-docker-list ix -G)"\" | grep -vE \""$(get-docker-list tp -G)"\""
if [ -z "$LIST" ] && [ -z "$SRC" ]; then
ARG="-c"
continue
fi
;;
*)
printf '%s %-12s %s\n' \
"Usage:" "" ""\
" ${BASE_NAME} ls <options>" "" ""\
"" "" "" \
"Available options:" "" ""\
"" "-G" "Create grep -E matching logic" \
"" "-a" "All containers (including inactive ones)" \
"" "-r" "Runing containers only" \
"" "+X" "for IX apps - preserve the ix- prefix" \
"" "" "" \
"" "ix|truenas" "IX (TrueNAS SCALE) apps" \
"" "tp|runtipi" "Runtipi apps" \
"" "other" "All other apps" \
&& return 1
;;
esac
shift
ARG="${1}"
done
if [ 0 -eq "${#LIST}" ]; then
LIST="${SRC}"
SRC=''
fi
if [ 0 -eq "${#LIST}" ]; then
printf "Source missing, must specify one of the following: -a | -r | ix | tp | other\n" >&2
return 1
fi
POST_PROCESS="${POST_PROCESS/\#PP1/${PP1}}"
POST_PROCESS="${POST_PROCESS/\#PP2/${PP2}}"
POST_PROCESS="${POST_PROCESS/\#PP3/${PP3}}"
POST_PROCESS="${POST_PROCESS/\#PP_SORT/${PP_SORT}}"
[ -n "$SRC" ] \
&& CLI="$SRC | grep -E \"\$(($LIST) $MK_SEARCH_LIST)\"" \
|| CLI="($LIST)"
CLI="$CLI ${FILTER:+| $FILTER} ${POST_PROCESS}"
printf '%s\n' $(eval "$CLI")
}
list-docker-apps() {
local LIST="$(get-docker-list "$@")"
local RUNNING="$(get-docker-list "$@" -r | sort-tipi)"
local LIMBO="$(get-docker-list "$@" -a | grep -xF "$LIST" | grep -vxF "$RUNNING" | sort-tipi)"
local DOWN="$(grep -Fxv -f <(printf '%s\n' "$RUNNING" "$LIMBO") <<<"$LIST" | sort-tipi)"
printf 'RUNNING\n' && printf ' %s\n' ${RUNNING:-"<nothing>"}
[ -n "$LIMBO" ] && printf '\nLIMBO \n' && printf ' %s\n' $LIMBO || true
[ -n "$DOWN" ] && printf '\nDOWN \n' && printf ' %s\n' $DOWN || true
}
runtipi-app-docker-compose() {
if [ $# -eq 0 ]; then
list-docker-apps tp
return 1
fi
local APP="${1:-Must supply app name}"
${SUDO} docker compose \
$(append_file_param --env-file user-config/.env.local) \
$(append_file_param --env-file app-data/${APP}/app.env) \
$(append_file_param --env-file user-config/${APP}/app.env) \
--project-name ${APP} \
$(if [ "$APP" != "runtipi" ]; then
echo --file apps/${APP}/docker-compose.yml
append_file_param --file repos/29ca930bfdaffa1dfabf5726336380ede7066bc53297e3c0c868b27c97282903/apps/docker-compose.common.yml
append_file_param --file "user-config/${APP}/docker-compose.yml"
else
append_file_param --file "docker-compose.yml"
append_file_param --file "user-config/tipi-compose.yml"
fi) \
${2:-ps -a} ${3:+"${@:3}"}
}
ix-app-docker-compose() {
if [ $# -eq 0 ]; then
list-docker-apps ix
return 1
fi
local APP="${1:-Must supply app name}"
DCSRC="$(
${SUDO} find /mnt/.ix-apps/app_configs/ -type f \
-path "*/${APP}/*/rendered/docker-compose.yaml" -printf '%T@ %p\n' \
| sort -n | cut -d' ' -f2- | head -1
)"
set -x
${SUDO} docker compose "--file=${DCSRC}" "--project-name=ix-${APP}" ${2:-ps -a} ${3:+"${@:3}"}
}
link-exists() {
local EXIT_CODE=0
printf 'Current status:\n link: '
${SUDO} ip -br -c link show "${1:?Must supply device name}" 2>&1 || return 1
}
manage-ipvlan() {
local IPVLAN_IF="${2:-ipvlan-lan}"
local NET_IP="${NET_IP:-${INTERNAL_IP:?}}"
local IP_BASE="${NET_IP%\.[[:digit:]]*}"
local NET_IF="${NET_IF:-"$(ip -4 -br a s to "${NET_IP}" | cut -d' ' -f1)"}"
NET_IF="${NET_IF:-$($(ip -4 -br a s to "${IP_BASE}.0/24" | cut -d' ' -f1))}"
NET_IF="${NET_IF:?"Could not detect network interface for ${NET_IP}"}"
case "${1}" in
rm)
manage-ipvlan status "${IPVLAN_IF}" && ( \
${SUDO} ip link delete "${IPVLAN_IF}" \
&& printf '%s\n' "${IPVLAN_IF} removed"
) || return 1
;;
add)
link-exists "${IPVLAN_IF}" > /dev/null 2>&1 \
&& link-exists "${IPVLAN_IF}" || ( \
${SUDO} ip link add "${IPVLAN_IF}" link "${NET_IF}" type ipvlan mode l2 \
&& ${SUDO} ip addr add ${NET_IP} dev "${IPVLAN_IF}" \
&& ${SUDO} ip link set "${IPVLAN_IF}" up \
&& printf '%s\n' "${IPVLAN_IF} created"\
&& link-exists "${IPVLAN_IF}" \
|| ${SUDO} ip link delete "${IPVLAN_IF}"
)
;;
route)
local ROUTE_SCOPE="${3:?Must supply routing scope in the form of ###.###.###.###[/##]}"
manage-ipvlan add "${IPVLAN_IF}" > /dev/null 2>&1 \
&& for IP in "${@:3}"; do
# Only add route if it does not already exists
printf 'Adding route for %-15s ' "$IP"
${SUDO} ip route get "$IP" 2> /dev/null | grep -q "dev ${IPVLAN_IF} src ${NET_IP//\./\\.}" \
&& printf 'Skipping, already exists.' \
|| ${SUDO} ip route add "$IP" dev "${IPVLAN_IF}" \
&& printf '\n' \
|| (printf 'Could NOT add %s\n' "$IP" >&2; return 1)
done
link-exists "${IPVLAN_IF}"
;;
status)
link-exists "${IPVLAN_IF}" || return 1
;;
*)
printf '%s %-12s %s\n' \
"$(manage-ipvlan status)" "" ""\
"" "" ""\
"Usage:" "" ""\
" [SUDO=sudo] ${BASE_NAME} ipvlan <command>" "" ""\
"" "" "" \
"Available commands:" "" ""\
"" "" "" \
"" "fix" "implement ipvlan fix" \
"" "rm" "remove ipvlan fix" \
"" "status" "output interface status" \
return 0
;;
esac
( printf 'addr: ' && ip -4 -br -c addr show "${IPVLAN_IF}" \
&& printf 'Routes:\n' \
&& ${SUDO} ip -c route show dev "${IPVLAN_IF}" \
| awk '{print} END{if (NR==0) print "<none found>"}'
) 2>&1 | sed -e '/[^:]$/s/^/ /' \
|| return 1
# ip link show [ DEVICE | group GROUP ] [up] [master DEV] [vrf NAME] [type TYPE] [nomaster]
}
case "${1}" in
cli)
runtipi-cli "${@:2}"
;;
log|logs)
POSTGRES_PASSWORD=_ TIPI_VERSION=_ LOCAL_DOMAIN=_ DOMAIN=_ runtipi-app-docker-compose "${2:-runtipi}" logs ${3:+"${@:3}"}
;;
start)
ROOT_FOLDER_HOST="${RUNTIPI_ROOT}" RUNTIPI_APP_DATA_PATH="${RUNTIPI_ROOT}" \
runtipi-cli start --env-file user-config/.env.local ${2:---no-permissions}
;;
update)
if [ -z "${2}" ]; then
${SCRIPT_DIR}/checkver.sh
else
runtipi-cli update --env-file user-config/.env.local --no-permissions "${2:?Must supply version}" "${@:3}"
fi
;;
dls)
dls "${@:2}"
;;
ls)
get-docker-list "${@:2}"
;;
tpcompose)
runtipi-app-docker-compose "${@:2}"
;;
ixcompose)
ix-app-docker-compose "${@:2}"
;;
shell)
runtipi-app-docker-compose "${2:?}" exec ${5:+"${@:5}"} -it "${4:-${2}}" "${3:-bash}"
;;
ipvlan)
manage-ipvlan "${@:2}"
;;
setup)
ln -s $2 "$(cd -- "${SCRIPT_DIR}" && pwd)/${BASE_NAME}" "${3:-$HOME/.local/bin/}"
;;
edit)
${VISUAL:-${EDITOR:-vi}} $0
;;
_load)
echo "alias ${BASE_NAME}cd='cd \"$SCRIPT_DIR/..\"'"
;;
*)
printf '%s %-12s %s\n' \
"" "" ""\
"Usage:" "" ""\
" [SUDO=sudo] ${BASE_NAME} <command> [args...]" "" ""\
"" "" "" \
"Available commands:" "" ""\
"" "" "" \
"runtipi" "" ""\
"" "cli" "runtipi-cli" \
"" "log" "runtipi docker stack logs" \
"" "start" "start runtipi" \
"" "update" "update runtipi to a specific version" \
"" "" "" \
"docker/docker-compose" "" ""\
"" "tpcompose" "docker compose for runtipi apps" \
"" "ixcompose" "docker compose for ix/TrueNAS SCALE docker based app" \
"" "ls" "list applications (or contrainers)" \
"" "dls" "stylized docker ls" \
"" "shell" "enter an insteractive shell" \
"" "down-all" "stop and remove everything" \
"" "" "" \
"networking" "" ""\
"" "ipvlan" "manage ipvlan networking interface fix" \
"" "" "" \
"misc." "" ""\
"" "exec" "execute within the shell, START_DIR env applies" \
"" "" "" \
"" "setup" "setup runtipictl in user's .local/bin dir" \
"" "" "${BASE_NAME} setup" \
"" "" "${BASE_NAME} setup '' ~/.local/bin/${BASE_BASE_NAME}" \
"" "" "${BASE_NAME} setup '' ~/.local/bin/runtipictl" \
"" "" "" \
"Related env. vars:" "" "" \
"" "VISUAL EDITOR RPH_UID QUIET START_DIR ROOT_EXEC" ""
;;
esac
# vim: set ft=sh expandtab tabstop=4 shiftwidth=4: