You've already forked Proxmox-Container-Template
Initialize Git Repository: 'Proxmox-Container-Template'
All checks were successful
Proxmox LXC Template Tool (Noble) / Proxmox LXC Template Tool [arm64] (push) Successful in 11s
Proxmox LXC Template Tool (Noble) / Proxmox LXC Template Tool [amd64] (push) Successful in 12s
Proxmox LXC Template Tool (Trixie) / Proxmox LXC Template Tool [arm64] (push) Successful in 11s
Proxmox LXC Template Tool (Trixie) / Proxmox LXC Template Tool [amd64] (push) Successful in 11s
All checks were successful
Proxmox LXC Template Tool (Noble) / Proxmox LXC Template Tool [arm64] (push) Successful in 11s
Proxmox LXC Template Tool (Noble) / Proxmox LXC Template Tool [amd64] (push) Successful in 12s
Proxmox LXC Template Tool (Trixie) / Proxmox LXC Template Tool [arm64] (push) Successful in 11s
Proxmox LXC Template Tool (Trixie) / Proxmox LXC Template Tool [amd64] (push) Successful in 11s
This commit is contained in:
394
root/usr/sbin/pct-template
Normal file
394
root/usr/sbin/pct-template
Normal file
@@ -0,0 +1,394 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
|
||||
###
|
||||
#
|
||||
# Options Section
|
||||
#
|
||||
###
|
||||
|
||||
set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
|
||||
###
|
||||
#
|
||||
# Function Section
|
||||
#
|
||||
###
|
||||
|
||||
function bind_chroot () {
|
||||
message "Bind file system '/dev' ..."
|
||||
if ! /usr/bin/grep --quiet "${CT_REALPATH}/dev" '/proc/mounts'; then
|
||||
/usr/bin/mount --bind '/dev' --target "${CT_REALPATH}/dev" &> '/dev/null' || \
|
||||
error "\r\rBind file system '/dev' [ERROR]\n" \
|
||||
" => The file system '/dev' could not be mounted.\n"
|
||||
fi
|
||||
message "\r\rBind filesystem '/dev' [OK]\n"
|
||||
message "Bind file system '/dev/pts' ..."
|
||||
if ! /usr/bin/grep --quiet "${CT_REALPATH}/dev/pts" '/proc/mounts'; then
|
||||
/usr/bin/mount --bind '/dev/pts' --target "${CT_REALPATH}/dev/pts" &> '/dev/null' || \
|
||||
error "\r\rBind file system '/dev/pts' [ERROR]\n" \
|
||||
" => The file system '/dev/pts' could not be mounted.\n"
|
||||
fi
|
||||
message "\r\rBind file system '/dev/pts' [OK]\n"
|
||||
message "Bind file system '/dev/mqueue' ..."
|
||||
if ! /usr/bin/grep --quiet "${CT_REALPATH}/dev/mqueue" '/proc/mounts'; then
|
||||
/usr/bin/mount --bind '/dev/mqueue' --target "${CT_REALPATH}/dev/mqueue" &> '/dev/null' || \
|
||||
error "\r\rBind files ystem '/dev/mqueue' [ERROR]\n" \
|
||||
" => The file system '/dev/mqueue' could not be mounted.\n"
|
||||
fi
|
||||
message "\r\rBind files ystem '/dev/mqueue' [OK]\n"
|
||||
message "Slave file system '/dev' ..."
|
||||
if ! /usr/bin/grep --quiet "${CT_REALPATH}/dev" '/proc/mounts'; then
|
||||
/usr/bin/mount --make-rslave --target "${CT_REALPATH}/dev" &> '/dev/null' || \
|
||||
error "\r\rSlave file system '/dev' [ERROR]\n" \
|
||||
" => The file system '/dev' could not be slaved.\n"
|
||||
fi
|
||||
message "\r\rSlave file system '/dev' [OK]\n"
|
||||
message "Bind file system '/proc' ..."
|
||||
if ! /usr/bin/grep --quiet "${CT_REALPATH}/proc" '/proc/mounts'; then
|
||||
/usr/bin/mount --types 'proc' --source '/proc' --target "${CT_REALPATH}/proc" &> '/dev/null' || \
|
||||
error "\r\rBind file system '/proc' [ERROR]\n" \
|
||||
" => The file system '/proc' could not be mounted.\n"
|
||||
fi
|
||||
message "\r\rBind file system '/proc' [OK]\n"
|
||||
message "Bind file system '/sys' ..."
|
||||
if ! /usr/bin/grep --quiet "${CT_REALPATH}/sys" '/proc/mounts'; then
|
||||
/usr/bin/mount --types 'sysfs' --source '/sys' --target "${CT_REALPATH}/sys" &> '/dev/null' || \
|
||||
error "\r\rBind file system '/sys' [ERROR]\n" \
|
||||
" => The file system '/sys' could not be mounted.\n"
|
||||
fi
|
||||
message "\r\rBind file system '/sys' [OK]\n"
|
||||
}
|
||||
|
||||
function consistency () {
|
||||
message 'Check container consistency ...'
|
||||
/usr/bin/test -d "${CT_REALPATH}" || \
|
||||
error '\r\rCheck container consistency [ERROR]\n' \
|
||||
' => The selected container does not exist.\n'
|
||||
/usr/bin/test -f "${CT_REALPATH}/etc/os-release" || \
|
||||
error '\r\rCheck container consistency [ERROR]\n' \
|
||||
' => The selected container does not contain any system files.\n'
|
||||
message '\r\rCheck container consistency [OK]\n'
|
||||
}
|
||||
|
||||
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 help () {
|
||||
local HELP
|
||||
case "${1}" in
|
||||
command)
|
||||
read -r -d '' HELP << EOL
|
||||
Usage: pct-template (OPTION) (CONTAINER)
|
||||
|
||||
Options:
|
||||
[ -c ] [ CONTAINER ] | Chroot Container
|
||||
[ -g ] [ CONTAINER ] | Generate Container Template
|
||||
[ -h ] | Help
|
||||
[ -p ] [ CONTAINER ] | Prepare Container
|
||||
[ -r ] [ CONTAINER ] | Container Repository
|
||||
EOL
|
||||
;;
|
||||
chroot)
|
||||
read -r -d '' HELP << EOL
|
||||
Usage: pct-template -c (CONTAINER)
|
||||
|
||||
Description:
|
||||
Change the root environment into the
|
||||
container.
|
||||
EOL
|
||||
;;
|
||||
generate)
|
||||
read -r -d '' HELP << EOL
|
||||
Usage: pct-template -g (CONTAINER)
|
||||
|
||||
Description:
|
||||
Compresses the container with the data
|
||||
compression format 'xz' and generates a
|
||||
hash sum of it.
|
||||
EOL
|
||||
;;
|
||||
prepare)
|
||||
read -r -d '' HELP << EOL
|
||||
Usage: pct-template -p (CONTAINER)
|
||||
|
||||
Description:
|
||||
Prepare and/or clean up the container.
|
||||
EOL
|
||||
;;
|
||||
repository)
|
||||
read -r -d '' HELP << EOL
|
||||
Usage: pct-template -r (CONTAINER)
|
||||
|
||||
Description:
|
||||
Download one of the following images from
|
||||
the repository.
|
||||
|
||||
- Debian GNU/Linux
|
||||
- arm64_debian_bookworm
|
||||
- arm64_debian_trixie
|
||||
- Ubuntu GNU/Linux
|
||||
- arm64_ubuntu_noble-numbat
|
||||
EOL
|
||||
;;
|
||||
esac
|
||||
/usr/bin/echo -e "${HELP}"
|
||||
}
|
||||
|
||||
function message () {
|
||||
if [ -n "${1:-}" ]; then
|
||||
/usr/bin/echo -n -e "${1}"
|
||||
fi
|
||||
}
|
||||
|
||||
function resolvconf () {
|
||||
local OPTION="${1}"
|
||||
case "${OPTION}" in
|
||||
prepare)
|
||||
message 'Prepare DNS-Resolver ...'
|
||||
if [[ -L "${CT_REALPATH}/etc/resolv.conf" ]]; then
|
||||
/usr/bin/unlink "${CT_REALPATH}/etc/resolv.conf" || \
|
||||
error '\r\rPrepare DNS-Resolver [ERROR]\n' \
|
||||
" => The 'resolv.conf' could not be unlinked.\n"
|
||||
fi
|
||||
/usr/bin/cp --force '/etc/resolv.conf' "${CT_REALPATH}/etc/resolv.conf" || \
|
||||
error '\r\rPrepare DNS-Resolver [ERROR]\n' \
|
||||
" => The 'resolv.conf' could not be copied into the container.\n"
|
||||
message '\r\rPrepare DNS-Resolver [OK]\n'
|
||||
;;
|
||||
reset)
|
||||
message 'Reset DNS-Resolver ...'
|
||||
if [[ -L "${CT_REALPATH}/etc/resolv.conf" ]]; then
|
||||
/usr/bin/unlink "${CT_REALPATH}/etc/resolv.conf" || \
|
||||
error '\r\rReset DNS-Resolver [ERROR]\n' \
|
||||
" => The 'resolv.conf' could not be unlinked.\n"
|
||||
fi
|
||||
/usr/bin/cp --force '/usr/share/pct-template/etc/resolv.conf' "${CT_REALPATH}/etc/resolv.conf" || \
|
||||
error '\r\rReset DNS-Resolver [ERROR]\n' \
|
||||
" => The file '${CT_REALPATH}/etc/resolv.conf' could not be replaced.\n"
|
||||
message '\r\rReset DNS-Resolver [OK]\n'
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
function unbind () {
|
||||
message "Unbind file system '/dev' ..."
|
||||
if /usr/bin/grep --quiet "${CT_REALPATH}/dev" '/proc/mounts'; then
|
||||
/usr/bin/umount --recursive "${CT_REALPATH}/dev" || \
|
||||
error "\r\rUnbind file system '/dev' [ERROR]\n" \
|
||||
" => The file system '/dev' could not be unmounted from the container.\n"
|
||||
fi
|
||||
message "\r\rUnbind file system '/dev' [OK]\n"
|
||||
message "Unbind file system '/proc' ..."
|
||||
if /usr/bin/grep --quiet "${CT_REALPATH}/proc" '/proc/mounts'; then
|
||||
/usr/bin/umount --recursive "${CT_REALPATH}/proc" || \
|
||||
error "\r\rUnbind file system '/proc' [ERROR]\n" \
|
||||
" => The file system '/proc' could not be unmounted from the container.\n"
|
||||
fi
|
||||
message "\r\rUnbind file system '/proc' [OK]\n"
|
||||
message "Unbind file system '/sys' ..."
|
||||
if /usr/bin/grep --quiet "${CT_REALPATH}/sys" '/proc/mounts'; then
|
||||
/usr/bin/umount --recursive "${CT_REALPATH}/sys" || \
|
||||
error "\r\rUnbind file system '/sys' [ERROR]\n" \
|
||||
" => The file system '/sys' could not be unmounted from the container.\n"
|
||||
fi
|
||||
message "\r\rUnbind file system '/sys' [OK]\n"
|
||||
}
|
||||
|
||||
|
||||
###
|
||||
#
|
||||
# Runtime Environment
|
||||
#
|
||||
###
|
||||
|
||||
/usr/bin/echo -e 'Proxmox LXC Template Tool\n'
|
||||
|
||||
if [[ "${EUID}" -ne '0' ]]; then
|
||||
/usr/bin/echo -e 'Error: Permission Denied'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
while getopts ':c:g:hp:r:' OPT; do
|
||||
case "${OPT}" in
|
||||
c)
|
||||
CT_BASENAME=$(/usr/bin/basename "${OPTARG}")
|
||||
CT_REALPATH=$(/usr/bin/realpath "${OPTARG}")
|
||||
consistency
|
||||
trap 'unbind' EXIT
|
||||
bind_chroot
|
||||
trap "resolvconf 'reset' && unbind" EXIT
|
||||
resolvconf 'prepare'
|
||||
message '\n ... Enter chroot environment ... \n\n'
|
||||
/usr/bin/sleep '3s'
|
||||
/usr/sbin/chroot "${CT_REALPATH}" || true
|
||||
message '\n ... Exit chroot environment ... \n\n'
|
||||
/usr/bin/sleep '3s'
|
||||
exit 0
|
||||
;;
|
||||
g)
|
||||
CT_BASENAME=$(/usr/bin/basename "${OPTARG}")
|
||||
CT_REALPATH=$(/usr/bin/realpath "${OPTARG}")
|
||||
CT_DIRNAME=$(/usr/bin/dirname "${CT_REALPATH}")
|
||||
consistency
|
||||
trap "/usr/bin/rm --force ${CT_DIRNAME}/${CT_BASENAME}.tar.xz"* EXIT
|
||||
message 'Generate container template ...'
|
||||
/usr/bin/tar --create --use-compress-program="/usr/bin/xz -9 --extreme" --file "${CT_DIRNAME}/${CT_BASENAME}.tar.xz" --directory="${CT_REALPATH}" '.' || \
|
||||
error '\r\rGenerate container template [ERROR]\n' \
|
||||
' => The container folder could not be compressed.\n'
|
||||
message '\r\rGenerate container template [OK]\n'
|
||||
message 'Generate container hashsum ...'
|
||||
/usr/bin/sha512sum "${CT_DIRNAME}/${CT_BASENAME}.tar.xz" | /usr/bin/sed --expression='s, .*/, ,' >> "${CT_DIRNAME}/${CT_BASENAME}.tar.xz.sha512sum" || \
|
||||
error '\r\rGenerate container hashsum [ERROR]\n' \
|
||||
' => The container hashsum could not be generated.\n'
|
||||
message '\r\rGenerate container hashsum [OK]\n'
|
||||
trap '' EXIT
|
||||
exit 0
|
||||
;;
|
||||
h)
|
||||
set +e
|
||||
help 'command'
|
||||
exit 0
|
||||
;;
|
||||
p)
|
||||
CT_BASENAME=$(/usr/bin/basename "${OPTARG}")
|
||||
CT_REALPATH=$(/usr/bin/realpath "${OPTARG}")
|
||||
CT_DIRNAME=$(/usr/bin/dirname "${CT_REALPATH}")
|
||||
consistency
|
||||
trap "/usr/bin/rm --force ${CT_DIRNAME}/.${CT_BASENAME}" EXIT
|
||||
message 'Prepare the planned file remove list ...'
|
||||
if [ -f "/usr/share/pct-template/prepare/${CT_BASENAME}" ]; then
|
||||
/usr/bin/cp --update='none' "/usr/share/pct-template/prepare/${CT_BASENAME}" "${CT_DIRNAME}/.${CT_BASENAME}" || \
|
||||
error '\r\rPrepare the planned file remove list [ERROR]\n' \
|
||||
" => The remove list '${CT_BASENAME}' could not be copied.\n"
|
||||
elif [[ ! -f "/usr/share/pct-template/prepare/${CT_BASENAME}" ]]; then
|
||||
/usr/bin/cp --update='none' "/usr/share/pct-template/prepare/default" "${CT_DIRNAME}/.${CT_BASENAME}" || \
|
||||
error '\r\rPrepare the planned file remove list [ERROR]\n' \
|
||||
" => The remove list 'default' could not be copied.\n"
|
||||
fi
|
||||
/usr/bin/editor "${CT_DIRNAME}/.${CT_BASENAME}" || \
|
||||
error '\r\rPrepare the planned file remove list [ERROR]\n' \
|
||||
" => The remove list '.${CT_BASENAME}' could not be opened.\n"
|
||||
message '\r\rPrepare the planned file remove list [OK]\n'
|
||||
message 'Remove files from remove list ...'
|
||||
mapfile -t CLEANUP_LIST < <(/usr/bin/cat "${CT_DIRNAME}/.${CT_BASENAME}" | /usr/bin/sed '/#.*/d' | while read -r CLEANUP_PATH; do /usr/bin/echo "${CT_REALPATH}${CLEANUP_PATH}"; done)
|
||||
for CLEANUP_LIST in "${CLEANUP_LIST[@]}"; do
|
||||
/usr/bin/sh -c "/usr/bin/ls --directory ${CLEANUP_LIST} | /usr/bin/xargs /usr/bin/rm --force --recursive" &> '/dev/null' || \
|
||||
error '\r\rRemove files from remove list [ERROR]\n' \
|
||||
" => The file or directory '${CLEANUP_LIST}' could not be removed.\n"
|
||||
done
|
||||
unset CLEANUP_LIST
|
||||
message '\r\rRemove files from remove list [OK]\n'
|
||||
message 'Cleaning up the log files ...'
|
||||
mapfile -t CLEANUP_LIST < <(/usr/bin/find "${CT_REALPATH}/var/log" -type 'f')
|
||||
for CLEANUP_LIST in "${CLEANUP_LIST[@]}"; do
|
||||
/usr/bin/dd if=/dev/null of="${CLEANUP_LIST}" &> '/dev/null' || \
|
||||
error '\r\rCleaning up the log files [ERROR]\n' \
|
||||
" => The file '${CLEANUP_LIST}' could not be emptied.\n"
|
||||
done
|
||||
unset CLEANUP_LIST
|
||||
mapfile -t CLEANUP_LIST < <(/usr/bin/find "${CT_REALPATH}/var/logrotate" -type 'f')
|
||||
for CLEANUP_LIST in "${CLEANUP_LIST[@]}"; do
|
||||
/usr/bin/sh -c "/usr/bin/ls --directory ${CLEANUP_LIST} | /usr/bin/xargs /usr/bin/rm --force --recursive" &> '/dev/null' || \
|
||||
error '\r\rCleaning up the log files [ERROR]\n' \
|
||||
" => The file '${CLEANUP_LIST}' could not be removed.\n"
|
||||
done
|
||||
message '\r\rCleaning up the log files [OK]\n'
|
||||
message 'Load container information ...'
|
||||
source "${CT_REALPATH}/etc/os-release" || \
|
||||
error '\r\rcontainer information [ERROR]\n' \
|
||||
" => The system information '/etc/os-release' could not be loaded.\n"
|
||||
message '\r\rLoad container information [OK]\n'
|
||||
message 'Prepare container hostname ...'
|
||||
/usr/bin/echo 'LXC_NAME' > "${CT_REALPATH}/etc/hostname" || \
|
||||
error '\r\rPrepare container hostname [ERROR]\n' \
|
||||
" => The file '/etc/hostname' could not be written.\n"
|
||||
message '\r\rPrepare container hostname [OK]\n'
|
||||
message 'Prepare container hosts ...'
|
||||
/usr/bin/cp --force '/usr/share/pct-template/etc/hosts' "${CT_REALPATH}/etc/hosts" || \
|
||||
error '\r\rPrepare container hosts [ERROR]\n' \
|
||||
" => The file '/etc/hosts' could not be replaced.\n"
|
||||
message '\r\rPrepare container hosts [OK]\n'
|
||||
case "${NAME}" in
|
||||
'Debian GNU/Linux')
|
||||
message 'Prepare container network interfaces ...'
|
||||
/usr/bin/cp --force '/usr/share/pct-template/etc/interfaces' "${CT_REALPATH}/etc/network/interfaces" || \
|
||||
error '\r\rPrepare container network interfaces [ERROR]\n' \
|
||||
" => The file '/etc/network/interfaces' could not be replaced.\n"
|
||||
message '\r\rPrepare container network interfaces [OK]\n'
|
||||
;;
|
||||
esac
|
||||
resolvconf 'reset'
|
||||
exit 0
|
||||
;;
|
||||
r)
|
||||
message 'Download container template ...'
|
||||
if [[ -d "./${OPTARG}" ]]; then
|
||||
error '\r\rDownload container template [ERROR]\n' \
|
||||
" => The container '${OPTARG}' already exists.\n"
|
||||
fi
|
||||
trap "/usr/bin/rm --force --recursive ./${OPTARG}.tar.xz"* EXIT
|
||||
/usr/bin/wget --quiet --output-document="./${OPTARG}.tar.xz" --no-hsts "https://repository.privlab.it/pct-template/${OPTARG}.tar.xz" &> '/dev/null' || \
|
||||
error '\r\rDownload container template [ERROR]\n' \
|
||||
" => The file '${OPTARG}.tar.xz' could not be downloaded.\n"
|
||||
/usr/bin/wget --quiet --output-document="./${OPTARG}.tar.xz.sha512sum" --no-hsts "https://repository.privlab.it/pct-template/${OPTARG}.tar.xz.sha512sum" &> '/dev/null' || \
|
||||
error '\r\rDownload container template [ERROR]\n' \
|
||||
" => The file '${OPTARG}.tar.xz.sha512sum' could not be downloaded.\n"
|
||||
message '\r\rDownload container template [OK]\n'
|
||||
message 'Security check of the container archive ...'
|
||||
/usr/bin/sha512sum --check --strict "./${OPTARG}.tar.xz.sha512sum" &> '/dev/null' || \
|
||||
error '\r\rSecurity check of the container archive [ERROR]\n' \
|
||||
" => The SHA512 hash value for '${OPTARG}.tar.xz' could not be verified.\n"
|
||||
message '\r\rSecurity check of the container archive [OK]\n'
|
||||
message 'Decompress container archive ...'
|
||||
/usr/bin/install --directory "./${OPTARG}" || \
|
||||
error '\r\rDecompress container archive [ERROR]\n' \
|
||||
" => The path '${OPTARG}' could not be installed.\n"
|
||||
/usr/bin/tar --extract --file="./${OPTARG}.tar.xz" --directory="./${OPTARG}" || \
|
||||
error '\r\rDecompress container archive [ERROR]\n' \
|
||||
" => The container archive '${OPTARG}.tar.xz' could not be decompressed.\n"
|
||||
/usr/bin/rm --force "./${OPTARG}.tar.xz" || \
|
||||
error '\r\rDecompress container archive [ERROR]\n' \
|
||||
" => The container archive '${OPTARG}.tar.xz' could not be removed.\n"
|
||||
message '\r\rDecompress container archive [OK]\n'
|
||||
trap "/usr/bin/rm --force --recursive ./${OPTARG}.tar.xz.sha512sum"* EXIT
|
||||
exit 0
|
||||
;;
|
||||
-*|*)
|
||||
set +e
|
||||
case "${1}" in
|
||||
-a)
|
||||
help 'archive'
|
||||
exit 0
|
||||
;;
|
||||
-c)
|
||||
help 'chroot'
|
||||
exit 0
|
||||
;;
|
||||
-p)
|
||||
help 'prepare'
|
||||
exit 0
|
||||
;;
|
||||
-r)
|
||||
help 'repository'
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
help 'command'
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
set +e
|
||||
help 'command'
|
||||
shift $((OPTIND-1))
|
||||
Reference in New Issue
Block a user