Initialize Git Repository: 'Proxmox-Container-Template'
Proxmox LXC Template Tool / Proxmox LXC Template Tool (amd64, cicd.any, bookworm trixie, pve8 pve9) (push) Successful in 10s
Proxmox LXC Template Tool / Proxmox LXC Template Tool (arm64, cicd.any, bookworm trixie, pve8 pve9) (push) Successful in 11s

This commit is contained in:
Cantibra
2026-05-04 21:21:55 +02:00
commit a710fb99e2
9 changed files with 1091 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
Package: pct-template
Version:
Architecture:
Installed-Size:
Depends: findutils, wget, xz-utils
Priority: optional
Section: misc
Homepage: http://www.privlab.it
Maintainer: PrivLab <repository@privlab.it>
Description: Proxmox Linux Containers Template Tool
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/sh
set -e
set -u
case "${1}" in
configure)
/usr/bin/install --directory --mode='750' '/var/lib/pct-templates'
;;
esac
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/sh
set -e
set -u
case "${1}" in
purge)
/usr/bin/rm --force --recursive '/var/lib/pct-templates'
;;
esac
+721
View File
@@ -0,0 +1,721 @@
#!/usr/bin/env bash
###
#
# Options Section
#
###
set -e
set -u
set -o pipefail
###
#
# Variable Section
#
###
REPOSITORY_URL='https://repository.privlab.it/archive/proxmox'
REPOSITORY_PATH='/var/lib/pct-templates'
###
#
# Function Section
#
###
function configuration () {
local CONFIGURATION_REALPATH="${1}"
local CONFIGURATION_BASENAME
CONFIGURATION_BASENAME=$(/usr/bin/basename "${CONFIGURATION_REALPATH}")
case "${CONFIGURATION_BASENAME}" in
cleanup)
/usr/bin/tee "${CONFIGURATION_REALPATH}" &> '/dev/null' <<EOF
## Cleanup Path Specification
## Absolute path to the files or folders
## which will be deleted from the template.
##
/dev/*
/lost+found/*
/media/*
/mnt/*
/proc/*
/run/*
/sys/*
/tmp/*
/tmp/.??*
/var/tmp/*
EOF
;;
resolv.conf)
/usr/bin/tee "${CONFIGURATION_REALPATH}" &> '/dev/null' <<EOF
# --- BEGIN PVE ---
# --- END PVE ---
EOF
;;
.version)
local DATE
DATE=$(/usr/bin/date +%s)
/usr/bin/tee "${CONFIGURATION_REALPATH}" &> '/dev/null' <<EOF
${DATE}
EOF
;;
esac
}
function error () {
if [[ -n "${1:-}" ]]; then
/usr/bin/echo -n -e "${1}"
fi
if [[ -n "${2:-}" ]]; then
/usr/bin/echo -n -e "${2}"
fi
exit 1
}
function consistency () {
local CHROOT_PATH="${1}"
/usr/sbin/chroot "${CHROOT_PATH}" /bin/sh -c 'exit 0'
}
function help () {
local HELP="${1}"
case "${HELP}" in
archive)
read -r -d '' HELP << EOL
Usage: pct-template -a (Template)
Description:
Archives a Proxmox container template from the local repository,
computes its checksum, and saves it to the current directory.
Templates available in the local repository can be listed
with the command 'pct-template -i'.
EOL
;;
chroot)
read -r -d '' HELP << EOL
Usage: pct-template -c (Image / Storage / Template)
Description:
Opens a CHROOT environment for a Proxmox container template.
The following host filesystems are binded and slaved into the CHROOT environment:
- 'devtmpfs' : '/dev'
- 'mqueue' : '/dev/mqueue'
- 'devpts' : '/dev/pts'
- 'proc' : '/proc'
- 'sys' : '/sys'
- 'tmpfs' : '/run'
All mounted filesystems are cleanly unmounted when exiting the CHROOT environment.
EOL
;;
download)
read -r -d '' HELP << EOL
Usage: pct-template -d (Template)
Description:
Downloads a Proxmox container template from the online repository or updates it.
If the local template is a newer version than the online repository,
user interaction to delete the local template is required before overwriting.
If the version in the online repository is newer, the local template is replaced
by the version from the online repository.
EOL
;;
generate)
read -r -d '' HELP << EOL
Usage: pct-template -g (Image)
Generate a Proxmox container template.
It first checks whether the required template is available in the local repository;
if not, it attempts to retrieve it from the online repository.
EOL
;;
help)
read -r -d '' HELP << EOL
Usage: pct-template (Option) (Image / Storage / Template)
Options:
[ -a ] [ Template ] | Archive a Proxmox container template
[ -c ] [ Template ] | Open a chroot environment in a Proxmxo container template
[ -d ] [ Template ] | Dowload / Update a Proxmox container template to local repository
[ -g ] [ Template ] | Generate a Promxox container template
[ -h ] | Display help
[ -i ] | Display repository information
[ -m ] [ Template ] | Mount / Umount a Proxmox container template
[ -u ] [ Template ] | Update an prepare Proxmox container template to a new version
EOL
;;
info)
read -r -d '' HELP << EOL
Usage: pct-template -i
Description:
Lists all available Proxmox container templates from the local and
online repositories and compares their versions.
EOL
;;
mount)
read -r -d '' HELP << EOL
Usage: pct-template -m (Image / Storage)
Description:
Mount at path '/mnt' either a Proxmox container image or a storage filesystem;
if the Proxmox container image or storage filesystem is already mounted, unmount it.
EOL
;;
update)
read -r -d '' HELP << EOL
Usage: pct-template -u (Template)
Description:
An editor opens displaying a list of files and folders to be removed from the
Proxmox container template. After removal, the template version is updated accordingly.
Since this Proxmox container template is, in a broad sense, a fork, it should be
considered whether renaming it would be appropriate.
EOL
;;
esac
/usr/bin/echo -e "${HELP}"
}
function template_download () {
local TEMPLATE="${1}"
local TEMPLATE_ONLINE_VERSION
local RESTORE_DIRECTORY
RESTORE_DIRECTORY=$(/usr/bin/pwd)
output "Download Proxmox Container template '${TEMPLATE}' ..."
TEMPLATE_ONLINE_VERSION=$(
/usr/bin/wget --quiet --output-document='-' "${REPOSITORY_URL}" 2> '/dev/null' | \
/usr/bin/tr '\n' ' ' | \
/usr/bin/sed --expression='s/</\n</g' | \
/usr/bin/grep --ignore-case --extended-regexp '^<a ' | \
/usr/bin/sed --quiet --regexp-extended 's/.*href=["'"'"']?([^"'"'"' >]+).*/\1/p' | \
/usr/bin/sed --expression='s,^\./,,; s/\?.*$//' | \
/usr/bin/grep --invert-match --extended-regexp '^(\.\./|/)$' | \
/usr/bin/grep --invert-match '/$' | \
/usr/bin/sed --expression='s/%20/ /g' | \
/usr/bin/uniq | \
/usr/bin/grep --extended-regexp "${TEMPLATE}\.[0-9]+\.tar\.xz$"
) || \
error "\r\rDownload Proxmox container template '${TEMPLATE}' [ERROR]\n" \
" => The online repository is not reachable.\n"
TEMPLATE_ONLINE_VERSION=$(/usr/bin/awk -F '.' '{print $NF}' <<< "${TEMPLATE_ONLINE_VERSION%.tar.xz}")
/usr/bin/wget --quiet \
--output-document="${TMP}/${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz" \
--no-hsts "${REPOSITORY_URL}/${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz" &> '/dev/null' || \
error "\r\rDownload Proxmox container template '${TEMPLATE}' [ERROR]\n" \
" => The online repository is not reachable.\n"
/usr/bin/wget --quiet \
--output-document="${TMP}/${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz.sha512sum" \
--no-hsts "${REPOSITORY_URL}/${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz.sha512sum" &> '/dev/null' || \
error "\r\rDownload Proxmox container template '${TEMPLATE}' [ERROR]\n" \
" => The online repository is not reachable.\n"
output "\r\rDownload Proxmox container template '${TEMPLATE}' [OK]\n"
output "Check the consistency of '${TEMPLATE}' ..."
cd "${TMP}"
/usr/bin/sha512sum --check \
--strict "${TMP}/${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz.sha512sum" &> '/dev/null' || \
error "\r\rCheck the consistency of '${TEMPLATE}' [ERROR]\n" \
" => The Proxmox container template '${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz' does not match the sha512 checksum.\n"
cd "${RESTORE_DIRECTORY}"
output "\r\rCheck the consistency of '${TEMPLATE}' [OK]\n"
output "Decompress Proxmox container template '${TEMPLATE}' ..."
/usr/bin/install --directory "${REPOSITORY_PATH}/${TEMPLATE}" || \
error "\r\rDecompress Proxmox container template '${TEMPLATE}' [ERROR]\n" \
" => The Proxmox container template directory '${REPOSITORY_PATH}/${TEMPLATE}' could not be installed.\n"
/usr/bin/tar --extract \
--directory="${REPOSITORY_PATH}/${TEMPLATE}" \
--file="${TMP}/${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz" || \
error "\r\rDecompress Proxmox container template '${TEMPLATE}' [ERROR]\n" \
" => The Proxmox container template '${TEMPLATE}.${TEMPLATE_ONLINE_VERSION}.tar.xz' could not be decompressed.\n"
output "\r\rDecompress Proxmox container template '${TEMPLATE}' [OK]\n"
}
function mount_api () {
local IMAGE_STORAGE_REALPATH="${1}"
local IMAGE_STORAGE_BASENAME
IMAGE_STORAGE_BASENAME=$(/usr/bin/basename "${IMAGE_STORAGE_REALPATH}")
local MOUNTPOINT_PREFIX="/mnt/${IMAGE_STORAGE_BASENAME}"
output "Mount 'devtmpfs' ..."
/usr/bin/mount --bind '/dev' \
--target "${MOUNTPOINT_PREFIX}/dev" &> '/dev/null' || \
error "\r\rMount 'devtmpfs' [ERROR]\n" \
" => The device 'devtmpfs' could not be mounted in '${MOUNTPOINT_PREFIX}/dev'.\n"
/usr/bin/mount --make-rslave \
--target "${MOUNTPOINT_PREFIX}/dev" &> '/dev/null' || \
error "\r\rMount 'devtmpfs' [ERROR]\n" \
" => The mountpoint '${MOUNTPOINT_PREFIX}/dev' could not be slaved.\n"
output "\r\rMount 'devtmpfs' [OK]\n"
output "Mount 'devpts' ..."
/usr/bin/mount --bind '/dev/pts' \
--target "${MOUNTPOINT_PREFIX}/dev/pts" &> '/dev/null' || \
error "\r\rMount 'devpts' [ERROR]\n" \
" => The device 'devpts' could not be mounted in '${MOUNTPOINT_PREFIX}/dev/pts'.\n"
/usr/bin/mount --make-rslave \
--target "${MOUNTPOINT_PREFIX}/dev/pts" &> '/dev/null' || \
error "\r\rSlave mountpoint 'devpts' [ERROR]\n" \
" => The mountpoint '${MOUNTPOINT_PREFIX}/dev/pts' could not be slaved.\n"
output "\r\rMount 'devpts' [OK]\n"
output "Mount 'mqueue' ..."
/usr/bin/mount --bind '/dev/mqueue' \
--target "${MOUNTPOINT_PREFIX}/dev/mqueue" &> '/dev/null' || \
error "\r\rMount 'mqueue' [ERROR]\n" \
" => The device 'mqueue' could not be mounted in '${MOUNTPOINT_PREFIX}/dev/mqueue'.\n"
/usr/bin/mount --make-rslave \
--target "${MOUNTPOINT_PREFIX}/dev/mqueue" &> '/dev/null' || \
error "\r\rMount 'mqueue' [ERROR]\n" \
" => The mountpoint '${MOUNTPOINT_PREFIX}/dev/mqueue' could not be slaved.\n"
output "\r\rMount 'mqueue' [OK]\n"
output "Mount 'proc' ..."
/usr/bin/mount --types 'proc' \
--source '/proc' \
--target "${MOUNTPOINT_PREFIX}/proc" &> '/dev/null' || \
error "\r\rMount 'proc' [ERROR]\n" \
" => The device 'proc' could not be mounted in '${MOUNTPOINT_PREFIX}/proc'.\n"
output "\r\rMount 'proc' [OK]\n"
output "Mount 'run' ..."
/usr/bin/mount --bind '/run' --target "${MOUNTPOINT_PREFIX}/run" || \
error "\r\rMount 'run' [ERROR]\n" \
" => The device 'sysfs' could not be mounted in '${MOUNTPOINT_PREFIX}/run'.\n"
/usr/bin/mount --make-rslave \
--target "${MOUNTPOINT_PREFIX}/run" &> '/dev/null' || \
error "\r\rSlave mountpoint 'tmpfs' [ERROR]\n" \
" => The mountpoint '${MOUNTPOINT_PREFIX}/dev/pts' could not be slaved.\n"
output "\r\rMount 'run' [OK]\n"
output "Mount 'sys' ..."
/usr/bin/mount --types 'sysfs' \
--source '/sys' \
--target "${MOUNTPOINT_PREFIX}/sys" &> '/dev/null' || \
error "\r\rMount 'sys' [ERROR]\n" \
" => The device 'sysfs' could not be mounted in '${MOUNTPOINT_PREFIX}/sys'.\n"
output "\r\rMount 'sys' [OK]\n"
}
function mount_template () {
local TEMPLATE_REALPATH="${1}"
local TEMPLATE_BASENAME
TEMPLATE_BASENAME=$(/usr/bin/basename "${TEMPLATE_REALPATH}")
output "Prepare mount point for '${TEMPLATE_BASENAME}' ..."
/usr/bin/install --directory "/mnt/${TEMPLATE_BASENAME}" || \
error "\r\rPrepare mount point for '${TEMPLATE_BASENAME}' [ERROR]\n" \
" => The mount point '/mnt/${TEMPLATE_BASENAME}' could not be installed.\n"
output "\r\rPrepare mount point for '${TEMPLATE_BASENAME}' [OK]\n"
output "Mount '${TEMPLATE_BASENAME}' ..."
/usr/bin/mount --bind "${TEMPLATE_REALPATH}" \
--target "/mnt/${TEMPLATE_BASENAME}" || \
error "\r\rMount '${TEMPLATE_BASENAME}' [ERROR]\n" \
" => The template '${TEMPLATE_BASENAME}' could not be mounted in '/mnt/${TEMPLATE_BASENAME}'.\n"
output "\r\rMount '${TEMPLATE_BASENAME}' [OK]\n"
}
function output () {
if [[ -n "${1:-}" ]]; then
/usr/bin/echo -n -e "${1}"
fi
}
function table_padding () {
local WIDTH="${1}"
shift
local ELLIPSIS="${1:-}"
/usr/bin/printf "%-*s" "${WIDTH}" "${ELLIPSIS}"
}
function umount_template () {
local TEMPLATE_REALPATH
local TEMPLATE_BASENAME
TEMPLATE_REALPATH="${1}"
TEMPLATE_BASENAME=$(/usr/bin/basename "${TEMPLATE_REALPATH}")
output "Unmount '${TEMPLATE_BASENAME}' ..."
/usr/bin/umount --recursive "/mnt/${TEMPLATE_BASENAME}" || \
error "\r\rUnmount '${TEMPLATE_BASENAME}' [ERROR]\n" \
" => The template '${TEMPLATE_BASENAME}' could not be unmounted from '/mnt/${TEMPLATE_BASENAME}'\n"
output "\r\rUnmount '${TEMPLATE_BASENAME}' [OK]\n"
if [[ -d "/mnt/${TEMPLATE_BASENAME}" ]]; then
output "Remove mountpoint '/mnt/${TEMPLATE_BASENAME}' ..."
/usr/bin/rm --force \
--dir "/mnt/${TEMPLATE_BASENAME}" || \
error "\r\rRemove mountpoint '/mnt/${TEMPLATE_BASENAME}' [ERROR]\n" \
" => The mountpoint '/mnt/${TEMPLATE_BASENAME}' could not be removed.\n"
output "\r\rRemove mountpoint '/mnt/${TEMPLATE_BASENAME}' [OK]\n"
fi
}
###
#
# Runtime Environment
#
###
/usr/bin/echo -e 'Proxmox Container Template Tool\n'
while getopts ':a:c:d:g:him:u:' OPT; do
case "${OPT}" in
a)
INPUT="${OPTARG}"
TEMPLATE_REALPATH="${REPOSITORY_PATH}/${INPUT}"
TEMPLATE_BASENAME=$(/usr/bin/basename "${TEMPLATE_REALPATH}") || \
error "\r\rArchive template '${INPUT}' [ERROR]\n" \
" => A Proxmox container template with the name '${INPUT}' does not exists.\n"
/usr/bin/test -f "${REPOSITORY_PATH}/${TEMPLATE_BASENAME}/.version" && \
TEMPLATE_LOCAL_VERSION=$(/usr/bin/cat "${REPOSITORY_PATH}/${TEMPLATE_BASENAME}/.version")
consistency "${TEMPLATE_REALPATH}"
output "Archive template '${TEMPLATE_BASENAME}' ..."
/usr/bin/tar --create \
--use-compress-program="/usr/bin/xz -9 --extreme" \
--file "./${TEMPLATE_BASENAME}.${TEMPLATE_LOCAL_VERSION}.tar.xz" \
--directory="${TEMPLATE_REALPATH}" '.' || \
error "\r\rArchive template '${TEMPLATE_BASENAME}' [ERROR]\n" \
" => The template '${TEMPLATE_BASENAME}' could not be archived.\n"
output "\r\rArchive template '${TEMPLATE_BASENAME}' [OK]\n"
output "Generate hashsum ..."
/usr/bin/sha512sum "./${TEMPLATE_BASENAME}.${TEMPLATE_LOCAL_VERSION}.tar.xz" | \
/usr/bin/sed --expression='s, .*/, ,' >> "./${TEMPLATE_BASENAME}.${TEMPLATE_LOCAL_VERSION}.tar.xz.sha512sum" || \
error '\r\rGenerate hashsum [ERROR]\n' \
' => The hashsum for the archived template could not be generated.\n'
output '\r\rGenerate hashsum [OK]\n'
exit 0
;;
c)
INPUT="${OPTARG}"
if [[ -d "${REPOSITORY_PATH}/${INPUT}" ]] && \
[[ ! -d "./${INPUT}" ]]; then
INPUT_REALPATH=$(/usr/bin/realpath "${REPOSITORY_PATH}/${INPUT}")
else
INPUT_REALPATH=$(/usr/bin/realpath "${INPUT}")
fi
consistency "${INPUT_REALPATH}"
TEMPLATE_REALPATH="${INPUT_REALPATH}"
TEMPLATE_BASENAME=$(/usr/bin/basename "${TEMPLATE_REALPATH}")
if /usr/bin/grep --word-regexp --quiet "${TEMPLATE_BASENAME}" '/proc/mounts' ; then
TEMPLATE_MOUNTED="1"
fi
if [[ -z "${TEMPLATE_MOUNTED:-}" ]]; then
mount_template "${TEMPLATE_REALPATH}"
mount_api "${TEMPLATE_REALPATH}"
output '\n ... Open chroot environment ... \n\n'
/usr/bin/ln --force --symbolic "/run/systemd/resolve/stub-resolv.conf" "/mnt/${TEMPLATE_BASENAME}/etc/resolv.conf" || \
error "\r\r... Open chroot environment ... [ERROR]\n" \
" => The 'resolv.conf' could not be prepared.\n"
/usr/bin/sleep '3s'
/usr/sbin/chroot "/mnt/${TEMPLATE_BASENAME}" || \
/usr/bin/true
output '\n ... Close chroot environment ... \n\n'
/usr/bin/unlink "/mnt/${TEMPLATE_BASENAME}/etc/resolv.conf" || \
error "\r\r... Open chroot environment ... [ERROR]\n" \
" => The 'resolv.conf' could not be unlinked.\n"
configuration "/mnt/${TEMPLATE_BASENAME}/etc/resolv.conf" || \
error "\r\r... Open chroot environment ... [ERROR]\n" \
" => The 'resolv.conf' could not be prepared.\n"
umount_template "${TEMPLATE_REALPATH}"
elif [[ -n "${TEMPLATE_MOUNTED}" ]]; then
for PID in $(/usr/bin/ps -e -o 'pid='); do
if [[ -e "/proc/${PID}/root" ]] && \
[[ "$(/usr/bin/readlink --canonicalize /proc/${PID}/root)" == "/mnt/${TEMPLATE_BASENAME}" ]]; then
error "\r\rOpen chroot environment for '${TEMPLATE_BASENAME}' [ERROR]\n" \
" => There is already a chroot envrionment for the Proxmox container template '${TEMPLATE_BASENAME}' opened.\n"
fi
done
umount_template "${TEMPLATE_REALPATH}"
mount_template "${TEMPLATE_REALPATH}"
mount_api "${TEMPLATE_REALPATH}"
output '\n ... Open chroot environment ... \n\n'
/usr/bin/ln --force --symbolic "/run/systemd/resolve/stub-resolv.conf" "/mnt/${TEMPLATE_BASENAME}/etc/resolv.conf" || \
error "\r\r... Open chroot environment ... [ERROR]\n" \
" => The 'resolv.conf' could not be prepared.\n"
/usr/bin/sleep '3s'
/usr/sbin/chroot "/mnt/${TEMPLATE_BASENAME}" || \
/usr/bin/true
output '\n ... Close chroot environment ... \n\n'
/usr/bin/unlink "/mnt/${TEMPLATE_BASENAME}/etc/resolv.conf" || \
error "\r\r... Open chroot environment ... [ERROR]\n" \
" => The 'resolv.conf' could not be unlinked.\n"
configuration "/mnt/${TEMPLATE_BASENAME}/etc/resolv.conf" || \
error "\r\r... Open chroot environment ... [ERROR]\n" \
" => The 'resolv.conf' could not be prepared.\n"
umount_template "${TEMPLATE_REALPATH}"
fi
exit 0
;;
d)
INPUT="${OPTARG}"
TMP=$(/usr/bin/mktemp --directory --quiet)
trap '/usr/bin/rm --force --recursive ${TMP}' EXIT
TEMPLATE_BASENAME="${INPUT}"
TEMPLATE_REALPATH="${REPOSITORY_PATH}/${TEMPLATE_BASENAME}"
TEMPLATE_ONLINE_VERSION=$(
/usr/bin/wget --quiet --output-document='-' "${REPOSITORY_URL}" 2> '/dev/null' | \
/usr/bin/tr '\n' ' ' | \
/usr/bin/sed --expression='s/</\n</g' | \
/usr/bin/grep --ignore-case --extended-regexp '^<a ' | \
/usr/bin/sed --quiet --regexp-extended 's/.*href=["'"'"']?([^"'"'"' >]+).*/\1/p' | \
/usr/bin/sed --expression='s,^\./,,; s/\?.*$//' | \
/usr/bin/grep --invert-match --extended-regexp '^(\.\./|/)$' | \
/usr/bin/grep --invert-match '/$' | \
/usr/bin/sed --expression='s/%20/ /g' | \
/usr/bin/uniq | \
/usr/bin/grep --extended-regexp "${TEMPLATE_BASENAME}\.[0-9]+\.tar\.xz$"
)
TEMPLATE_ONLINE_VERSION=$(/usr/bin/awk -F '.' '{print $NF}' <<< "${TEMPLATE_ONLINE_VERSION%.tar.xz}")
/usr/bin/test -f "${TEMPLATE_REALPATH}/.version" && \
TEMPLATE_LOCAL_VERSION=$(/usr/bin/cat "${TEMPLATE_REALPATH}/.version")
if [[ ! -d "${TEMPLATE_REALPATH}" ]]; then
template_download "${TEMPLATE_BASENAME}"
elif [[ -d "${TEMPLATE_REALPATH}" ]] && \
[[ "${TEMPLATE_LOCAL_VERSION}" -lt "${TEMPLATE_ONLINE_VERSION}" ]]; then
output "Remove Proxmox container template '${TEMPLATE_BASENAME}' ..."
/usr/bin/rm --force --recursive "${TEMPLATE_REALPATH}" || \
error "\r\rRemove Proxmox container template '${TEMPLATE_BASENAME}' [ERROR]\n" \
" => The Proxmox container template archive under '${TEMPLATE_REALPATH}' could not be removed.\n"
output "\r\rRemove Proxmox container template '${TEMPLATE_BASENAME}' [OK]\n"
template_download "${TEMPLATE_BASENAME}"
elif [[ -d "${TEMPLATE_REALPATH}" ]] && \
[[ "${TEMPLATE_LOCAL_VERSION}" -gt "${TEMPLATE_ONLINE_VERSION}" ]]; then
read -r -p $'The Proxmox container template in the local repository is newer\nthan the template in the online repository.\n\nWould you like to overwrite the local template? [Yes/No] ' ANSWER
if [[ "${ANSWER,,}" == "yes" ]]; then
output "Remove Proxmox container template '${TEMPLATE_BASENAME}' ..."
/usr/bin/rm --force --recursive "${TEMPLATE_REALPATH}" || \
error "\r\rRemove Proxmox container template '${TEMPLATE_BASENAME}' [ERROR]\n" \
" => The Proxmox container template under '${TEMPLATE_REALPATH}' could not be removed.\n"
output "\r\rRemove Proxmox container template '${TEMPLATE_BASENAME}' [OK]\n"
template_download "${TEMPLATE_BASENAME}"
else
exit 0
fi
elif [[ -d "${TEMPLATE_REALPATH}" ]] && \
[[ "${TEMPLATE_LOCAL_VERSION}" -eq "${TEMPLATE_ONLINE_VERSION}" ]]; then
error "The Proxmox container template '${TEMPLATE_BASENAME}' is already up to date.\n"
fi
exit 0
;;
g)
INPUT="${OPTARG}"
TMP=$(/usr/bin/mktemp --directory --quiet)
trap '/usr/bin/rm --force --recursive ${TMP}' EXIT
TEMPLATE_BASENAME="${OPTARG}"
TEMPLATE_REALPATH="${REPOSITORY_PATH}/${TEMPLATE_BASENAME}"
if [[ ! -d "${TEMPLATE_REALPATH}" ]]; then
template_download "${TEMPLATE_BASENAME}"
fi
/usr/bin/test -f "./${TEMPLATE_BASENAME}.tar.xz" && \
error '\r\rGenerate Proxmox container image [ERROR]\n' \
" => A Proxmox container template with the name '${TEMPLATE_BASENAME}' already exists.\n"
consistency "${TEMPLATE_REALPATH}"
output "Generate Proxmox container template '${TEMPLATE_BASENAME}' ..."
/usr/bin/tar --create \
--use-compress-program="/usr/bin/xz -9 --extreme" \
--exclude='.cleanup' \
--exclude='.version' \
--file "./${TEMPLATE_BASENAME}.tar.xz" \
--directory="${TEMPLATE_REALPATH}" '.' || \
error "\r\rGenerate Proxmox container template '${TEMPLATE_BASENAME}' [ERROR]\n" \
" => The Proxmox container template '${TEMPLATE_BASENAME}' could not be generated.\n"
output "\r\rGenerate Proxmox container template '${TEMPLATE_BASENAME}' [OK]\n"
output "Generate Proxmox container template hashsum ..."
/usr/bin/sha512sum "./${TEMPLATE_BASENAME}.tar.xz" | \
/usr/bin/sed --expression='s, .*/, ,' >> "./${TEMPLATE_BASENAME}.tar.xz.sha512sum" || \
error '\r\rGenerate Proxmox container template hashsum [ERROR]\n' \
' => The hashsum for the Proxmox container template could not be generated.\n'
output '\r\rGenerate Proxmox container template hashsum [OK]\n'
exit 0
;;
i)
if [[ -d "${REPOSITORY_PATH}" ]]; then
mapfile -t REPOSITORY_LOCAL_FILES < <(
/usr/bin/find "${REPOSITORY_PATH}/" -mindepth '1' -maxdepth '1' -type 'd' ! -name 'lost+found' -exec /usr/bin/basename {} \;
)
fi
mapfile -t REPOSITORY_ONLINE_FILES < <(
/usr/bin/wget --quiet --output-document='-' "${REPOSITORY_URL}" 2> '/dev/null' | \
/usr/bin/tr '\n' ' ' | \
/usr/bin/sed --expression='s/</\n</g' | \
/usr/bin/grep --ignore-case --extended-regexp '^<a ' | \
/usr/bin/sed --quiet --regexp-extended 's/.*href=["'"'"']?([^"'"'"' >]+).*/\1/p' | \
/usr/bin/sed --expression='s,^\./,,; s/\?.*$//' | \
/usr/bin/grep --invert-match --extended-regexp '^(\.\./|/)$' | \
/usr/bin/grep --invert-match '/$' | \
/usr/bin/sed --expression='s/%20/ /g' | \
/usr/bin/uniq | \
/usr/bin/grep --extended-regexp '\.tar\.xz$'
)
REPOSITORY_FILES_PIPE=("${REPOSITORY_LOCAL_FILES[@]}" "${REPOSITORY_ONLINE_FILES[@]}")
mapfile -t REPOSITORY_FILES_PIPE < <(
/usr/bin/printf '%s\n' "${REPOSITORY_FILES_PIPE[@]}" | \
/usr/bin/sort --key='1,1' \
--key='2,2r' \
--field-separator='.'
)
for REPOSITORY_FILE in "${REPOSITORY_FILES_PIPE[@]}"; do
REPOSITORY_FILES+=("${REPOSITORY_FILE%.tar.xz}")
done
/usr/bin/printf ' '
/usr/bin/printf '%.0s-' {1..108}
/usr/bin/printf "\n| %-40s | %-30s | %-30s | %s\n" "Proxmox container Template" "Verion (Local Repository)" "Version (Online Repository)"
/usr/bin/printf ' '
/usr/bin/printf "%.0s-" {1..108}
/usr/bin/printf "\n"
for REPOSITORY_FILE in "${REPOSITORY_FILES[@]}"; do
REPOSITORY_TEMPLATE="${REPOSITORY_FILE//.[0-9]*/}"
for REPOSITORY_TEMPLATE_DIFF in "${REPOSITORY_LOOP[@]:-}"; do
if [[ "$REPOSITORY_TEMPLATE" == "$REPOSITORY_TEMPLATE_DIFF" ]]; then
REPOSITORY_LOOP_BREAK='1'
break
fi
done
if [[ "${REPOSITORY_LOOP_BREAK:-}" -eq '1' ]]; then
unset REPOSITORY_LOOP_BREAK
continue
fi
REPOSITORY_LOOP+=("${REPOSITORY_TEMPLATE}")
if [[ "${REPOSITORY_FILE}" =~ \.([0-9]+) ]]; then
REPOSITORY_ONLINE_VERSION=$(/usr/bin/awk -F '.' '{print $NF}' <<< "${REPOSITORY_FILE}")
fi
if [[ -d "${REPOSITORY_PATH}/${REPOSITORY_TEMPLATE}" ]] && \
[[ -s "${REPOSITORY_PATH}/${REPOSITORY_TEMPLATE}/.version" ]]; then
REPOSITORY_LOCAL_VERSION=$(/usr/bin/cat "${REPOSITORY_PATH}/${REPOSITORY_TEMPLATE}/.version")
elif [[ -d "${REPOSITORY_PATH}/${REPOSITORY_TEMPLATE}" ]] && \
[[ ! -s "${REPOSITORY_PATH}/${REPOSITORY_TEMPLATE}/.version" ]]; then
REPOSITORY_LOCAL_VERSION='N/V'
fi
REPOSITORY_TEMPLATE_PADDING=$(table_padding '40' "${REPOSITORY_TEMPLATE:-}")
REPOSITORY_LOCAL_VERSION_PADDING=$(table_padding '30' "${REPOSITORY_LOCAL_VERSION=N/A}")
REPOSITORY_ONLINE_VERSION_PADDING=$(table_padding '30' "${REPOSITORY_ONLINE_VERSION:=N/A}")
REPOSITORY_TABLE_FORMAT_DIM=$'\e[2m'
REPOSITORY_TABLE_FORMAT_GREEN=$'\e[92m'
REPOSITORY_TABLE_FORMAT_RED=$'\e[91m'
REPOSITORY_TABLE_FORMAT_RESET=$'\e[0m'
if [[ -n "${REPOSITORY_LOCAL_VERSION:-}" ]] && \
[[ -n "${REPOSITORY_ONLINE_VERSION:-}" ]]; then
if [[ "${REPOSITORY_LOCAL_VERSION:-}" -lt "${REPOSITORY_ONLINE_VERSION:-}" ]]; then
REPOSITORY_LOCAL_VERSION_PADDING="${REPOSITORY_TABLE_FORMAT_RED}${REPOSITORY_LOCAL_VERSION_PADDING}${REPOSITORY_TABLE_FORMAT_RESET}"
REPOSITORY_ONLINE_VERSION_PADDING="${REPOSITORY_TABLE_FORMAT_GREEN}${REPOSITORY_ONLINE_VERSION_PADDING}${REPOSITORY_TABLE_FORMAT_RESET}"
elif [[ "${REPOSITORY_LOCAL_VERSION:-}" -gt "${REPOSITORY_ONLINE_VERSION:-}" ]]; then
REPOSITORY_LOCAL_VERSION_PADDING="${REPOSITORY_TABLE_FORMAT_GREEN}${REPOSITORY_LOCAL_VERSION_PADDING}${REPOSITORY_TABLE_FORMAT_RESET}"
REPOSITORY_ONLINE_VERSION_PADDING="${REPOSITORY_TABLE_FORMAT_RED}${REPOSITORY_ONLINE_VERSION_PADDING}${REPOSITORY_TABLE_FORMAT_RESET}"
fi
fi
if [[ "${REPOSITORY_LOCAL_VERSION:-}" == 'N/V' ]] || \
[[ "${REPOSITORY_LOCAL_VERSION:-}" == 'N/A' ]] || \
[[ -z "${REPOSITORY_LOCAL_VERSION:-}" ]]; then
REPOSITORY_LOCAL_VERSION_PADDING="${REPOSITORY_TABLE_FORMAT_DIM}${REPOSITORY_LOCAL_VERSION_PADDING}${REPOSITORY_TABLE_FORMAT_RESET}"
fi
if [[ -z "${REPOSITORY_ONLINE_VERSION:-}" ]]; then
REPOSITORY_ONLINE_VERSION_PADDING="${REPOSITORY_TABLE_FORMAT_DIM}${REPOSITORY_ONLINE_VERSION_PADDING}${REPOSITORY_TABLE_FORMAT_RESET}"
fi
/usr/bin/printf '| %s | %s | %s | %s\n' \
"${REPOSITORY_TEMPLATE_PADDING:-}" \
"${REPOSITORY_LOCAL_VERSION_PADDING:-}" \
"${REPOSITORY_ONLINE_VERSION_PADDING:-}"
unset REPOSITORY_ONLINE_VERSION
unset REPOSITORY_LOCAL_VERSION
done
/usr/bin/printf ' '
/usr/bin/printf "%.0s-" {1..108}
/usr/bin/printf "\n"
exit 0
;;
m)
INPUT="${OPTARG}"
if [[ -d "${REPOSITORY_PATH}/${INPUT}" ]] && \
[[ ! -d "./${INPUT}" ]]; then
INPUT_REALPATH=$(/usr/bin/realpath "${REPOSITORY_PATH}/${INPUT}")
else
INPUT_REALPATH=$(/usr/bin/realpath "${INPUT}")
fi
consistency "${INPUT_REALPATH}"
TEMPLATE_REALPATH="${INPUT_REALPATH}"
TEMPLATE_BASENAME=$(/usr/bin/basename "${TEMPLATE_REALPATH}")
if /usr/bin/grep --quiet "${TEMPLATE_BASENAME}" '/proc/mounts'; then
TEMPLATE_MOUNTED="1"
fi
if [[ -z "${TEMPLATE_MOUNTED:-}" ]]; then
mount_template "${TEMPLATE_REALPATH}"
elif [[ -n "${TEMPLATE_MOUNTED}" ]]; then
umount_template "${TEMPLATE_REALPATH}"
fi
exit 0
;;
u)
INPUT="${OPTARG}"
TEMPLATE_REALPATH=$(/usr/bin/realpath "${REPOSITORY_PATH}/${INPUT}")
TEMPLATE_BASENAME=$(/usr/bin/basename "${TEMPLATE_REALPATH}")
TMP=$(/usr/bin/mktemp --directory --quiet)
trap '/usr/bin/rm --force --recursive ${TMP}' EXIT
/usr/bin/grep --quiet "${TEMPLATE_REALPATH}" '/proc/mounts' && \
error "\r\rUpdate version of Proxmox container template '${TEMPLATE_BASENAME}' [ERROR]\n" \
" => The Proxmox container template '${TEMPLATE_BASENAME}' is mounted.\n"
consistency "${TEMPLATE_REALPATH}"
output 'Load configuration for the Proxmox container template cleanup ...'
if [[ -f "${TEMPLATE_REALPATH}/.cleanup" ]]; then
/usr/bin/cp --dereference "${TEMPLATE_REALPATH}/.cleanup" "${TMP}/cleanup" || \
error '\r\rLoad configuration for the Proxmox container template cleanup [ERROR]\n' \
" => The configuration template '{TEMPLATE_REALPATH}/.cleanup' could not be copied to '{TMP}/cleanup'.\n"
else
configuration "${TMP}/cleanup" || \
error '\r\rLoad configuration for the Proxmox container template cleanup [ERROR]\n' \
" => The default configuration template could not be copied to '{TMP}/cleanup'.\n"
fi
output '\r\rLoad configuration for the Proxmox container template cleanup [OK]\n'
output 'Remove configured files and folders ...'
mapfile -t TEMPLATE_REMOVE_LIST < <(
/usr/bin/sed '/#.*/d' < "${TMP}/cleanup" | \
while read -r TEMPLATE_REMOVE_PATH; do \
/usr/bin/echo "${TEMPLATE_REALPATH}${TEMPLATE_REMOVE_PATH}"; \
done
)
for X in "${TEMPLATE_REMOVE_LIST[@]}"; do
/usr/bin/sh -c "/usr/bin/ls --directory ${X} | /usr/bin/xargs /usr/bin/rm --force --recursive" &> '/dev/null' || \
error '\r\rRemove configured files and folders [ERROR]\n' \
" => The file or directory '${X}' could not be removed.\n"
done
output '\r\rRemove configured files and folders [OK]\n'
TEMPLATE_NEW_VERSION=$(/usr/bin/date +%s)
output "Update Proxmox container template verion to '${TEMPLATE_NEW_VERSION}' ..."
configuration "${TEMPLATE_REALPATH}/.version" || \
error "\r\rUpdate Proxmox container template verion to '${TEMPLATE_NEW_VERSION}' [ERROR]\n" \
" => The template version cloud not be updated.\n"
output "\r\rUpdate Proxmox container template verion to '${TEMPLATE_NEW_VERSION}' [OK]\n"
exit 0
;;
-*|*)
set +e
case "${1}" in
-a)
help 'archive'
exit 0
;;
-c)
help 'chroot'
exit 0
;;
-d)
help 'download'
exit 0
;;
-g)
help 'generate'
exit 0
;;
-i)
help 'info'
exit 0
;;
-m)
help 'mount'
exit 0
;;
-u)
help 'update'
exit 0
;;
esac
;;
esac
done
set +e
help 'help'
shift $((OPTIND-1))