You've already forked Proxmox-Container-Template
Initialize Git Repository: 'Proxmox-Container-Template'
This commit is contained in:
@@ -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))
|
||||
Reference in New Issue
Block a user