diff options
Diffstat (limited to 'libexec')
-rwxr-xr-x | libexec/container/auto | 54 | ||||
-rwxr-xr-x | libexec/container/build | 287 | ||||
-rwxr-xr-x | libexec/container/console | 5 | ||||
-rwxr-xr-x | libexec/container/enter | 7 | ||||
-rwxr-xr-x | libexec/container/get (renamed from libexec/container/create) | 25 | ||||
-rwxr-xr-x | libexec/container/info | 221 | ||||
-rwxr-xr-x | libexec/container/key | 31 | ||||
-rwxr-xr-x | libexec/container/limit | 5 | ||||
-rwxr-xr-x | libexec/container/list | 39 | ||||
-rwxr-xr-x | libexec/container/log | 5 | ||||
-rwxr-xr-x | libexec/container/move | 5 | ||||
-rwxr-xr-x | libexec/container/rebuild | 152 | ||||
-rwxr-xr-x | libexec/container/remove | 7 | ||||
-rwxr-xr-x | libexec/container/restart | 42 | ||||
-rwxr-xr-x | libexec/container/run | 3 | ||||
-rwxr-xr-x | libexec/container/start | 44 | ||||
-rwxr-xr-x | libexec/container/status | 3 | ||||
-rwxr-xr-x | libexec/container/stop | 84 | ||||
-rwxr-xr-x | libexec/container/top | 5 | ||||
-rwxr-xr-x | libexec/container/update | 270 | ||||
-rwxr-xr-x | libexec/container/version | 2 |
21 files changed, 1242 insertions, 54 deletions
diff --git a/libexec/container/auto b/libexec/container/auto index 0c9d136..83c5c50 100755 --- a/libexec/container/auto +++ b/libexec/container/auto @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (C) 2014-2021 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> # # SPDX-License-Identifier: GPL-3.0+ # @@ -76,6 +76,9 @@ Parameters () Usage () { echo "Usage: ${PROGRAM} ${COMMAND} -f|--force -s|--start -t|--stop" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + exit 1 } @@ -108,17 +111,50 @@ esac for FILE in "${CONFIG}"/*.conf do - if grep -Eqs "^ *cnt.auto=force-true" "${FILE}" + if ! grep -Eqs "^ *cnt.container-server=${HOST}" "${FILE}" then - OPTIONS="${OPTIONS} -f" + continue fi - if grep -Eqs "^ *cnt.auto=(force-true|true)" "${FILE}" && grep -Eqs "^ *cnt.container-server=${HOST}" "${FILE}" - then - CONTAINER="$(basename ${FILE} .conf)" - - cnt ${ACTION} -n ${CONTAINER} ${OPTIONS} || true - fi + CONTAINER="$(basename ${FILE} .conf)" + CNT_AUTO="$(grep -Es "^ *cnt.auto=" ${FILE} | awk -F= '{ print $2 }')" + + case "${ACTION}" in + start) + case "${CNT_AUTO}" in + force-true) + OPTIONS="${OPTIONS} -f" + + cnt ${ACTION} -n ${CONTAINER} ${OPTIONS} || true + ;; + + last-on) + if grep -qs start "/var/lib/${SOFTWARE}/state/${CONTAINER}.run" || \ + [ ! -e "/var/lib/${SOFTWARE}/state/${CONTAINER}.run" ] + then + cnt start -n ${CONTAINER} ${OPTIONS} -f || true + fi + ;; + + last-off) + if grep -qs start "/var/lib/${SOFTWARE}/state/${CONTAINER}.run" + then + cnt start -n ${CONTAINER} ${OPTIONS} -f || true + fi + ;; + + true) + cnt ${ACTION} -n ${CONTAINER} ${OPTIONS} || true + ;; + esac + ;; + + stop) + OPTIONS="${OPTIONS} -f --stateless" + + cnt ${ACTION} -n ${CONTAINER} ${OPTIONS} || true + ;; + esac done # Post hooks diff --git a/libexec/container/build b/libexec/container/build new file mode 100755 index 0000000..2c29730 --- /dev/null +++ b/libexec/container/build @@ -0,0 +1,287 @@ +#!/bin/sh + +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# +# SPDX-License-Identifier: GPL-3.0+ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +set -e + +PROJECT="open-infrastructure" +SOFTWARE="compute-tools" +PROGRAM="container" +COMMAND="$(basename ${0})" + +CONFIG="/etc/${SOFTWARE}/config" +HOOKS="/etc/${SOFTWARE}/hooks" +MACHINES="/var/lib/machines" +SCRIPTS="/usr/share/${SOFTWARE}/build-scripts" +CONFIG_TEMPLATE="/usr/share/${SOFTWARE}/config/container.conf.in" + +Parameters () +{ + GETOPT_LONGOPTIONS="name:,cnt.auto:,cnt.container-server:,cnt.overlay:,cnt.overlay-options:,cnt.start:,bind:,bind-ro:,capability:,drop-capability:,script:,verbose," + GETOPT_OPTIONS="n:,b:,c:,d:,s:,v," + + PARAMETERS="$(getopt --longoptions ${GETOPT_LONGOPTIONS} --name=${COMMAND} --options ${GETOPT_OPTIONS} --shell sh -- ${@})" + + if [ "${?}" != "0" ] + then + echo "'${COMMAND}': getopt exit" >&2 + exit 1 + fi + + eval set -- "${PARAMETERS}" + + while true + do + case "${1}" in + -n|--name) + NAME="${2}" + shift 2 + ;; + + --cnt.auto) + CNT_AUTO="${2}" + shift 2 + ;; + + --cnt.container-server) + CNT_CONTAINER_SERVER="${2}" + shift 2 + ;; + + --cnt.overlay) + CNT_OVERLAY="${2}" + shift 2 + ;; + + --cnt.overlay-options) + CNT_OVERLAY_OPTIONS="${2}" + shift 2 + ;; + + --cnt.start) + CNT_START="${2}" + shift 2 + ;; + + -b|--bind) + BIND="${2}" + shift 2 + ;; + + --bind-ro) + BIND_RO="${2}" + shift 2 + ;; + + -c|--capability) + CAPABILITY="${2}" + shift 2 + ;; + + -d|--drop-capability) + DROP_CAPABILITY="${2}" + shift 2 + ;; + + -s|--script) + SCRIPT="${2}" + shift 2 + ;; + + -v|--verbose) + VERBOSE="true" + shift 1 + ;; + + --) + shift 1 + break + ;; + + *) + echo "'${COMMAND}': getopt error" >&2 + exit 1 + ;; + esac + done +} + +Usage () +{ + echo "Usage: ${PROGRAM} ${COMMAND} -n|--name NAME [--cnt.auto=true|false|force-true|last-on|last-off] [--cnt.container-server=true|false|FQDN] [--cnt.overlay=DIRECTORY_LOWER:DIRECTORY_UPPER:DIRECTORY_WORK:DIRECTORY_MERGED] [--cnt.overlay-options=OPTION[,OPTION]] [--cnt.start=OPTION[,OPTION]] [-b|--bind DIRECTORY:DIRECTORY[:OPTIONS]] [--bind-ro DIRECTORY:DIRECTORY[:OPTIONS]] [-c|--capability CAPABILITY[,CAPABILITY]] [-d|--drop-capability DROP_CAPABILITY[,DROP_CAPABILITY]] [-s|--script SCRIPT] [-v|--verbose] [-- SCRIPT_OPTIONS]" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + + exit 1 +} + +Parameters "${@}" + +if [ -z "${NAME}" ] +then + Usage +fi + +case "${NAME}" in + ALL) + echo "'${NAME}': name 'ALL' is reserved to expand to all available container" >&2 + exit 1 + ;; +esac + +if [ -e "${CONFIG}/${NAME}.conf" ] +then + echo "'${NAME}': container already exists or ${CONFIG}/${NAME}.conf has not been removed" >&2 + exit 1 +fi + +if [ -z "${SCRIPT}" ] +then + if [ -e "${SCRIPTS}/default" ] + then + TARGET="$(basename $(readlink ${SCRIPTS}/default))" + + case "${TARGET}" in + container_build-script) + TARGET="$(basename $(readlink /etc/alternatives/container_build-script))" + ;; + esac + + if [ -e "${SCRIPTS}/${TARGET}" ] + then + SCRIPT="${TARGET}" + else + echo "default -> '${TARGET}': no such script" >&2 + exit 1 + fi + else + SCRIPT="debian" + fi +else + if [ ! -e "${SCRIPTS}/${SCRIPT}" ] + then + echo "'${SCRIPT}': no such script" >&2 + exit 1 + fi +fi + +case "${VERBOSE}" in + true) + +cat << EOF +################################################################################ +Building container: ${NAME} +################################################################################ +EOF + + ;; +esac + +CNT_CONTAINER_SERVER="${CNT_CONTAINER_SERVER:-$(hostname -f 2> /dev/null || hostname)}" + +# Pre hooks +for FILE in "${HOOKS}/pre-${COMMAND}".* "${HOOKS}/${NAME}.pre-${COMMAND}" +do + if [ -x "${FILE}" ] + then + "${FILE}" + fi +done + +# Creating rw bind mounts +if [ -n "${BIND}" ] +then + BINDS="$(echo ${BIND} | sed -e 's|;| |g')" + + for ENTRY in ${BINDS} + do + DIRECTORY="$(echo ${ENTRY} | awk -F: '{ print $1 }')" + + mkdir -p "${DIRECTORY}" + done +fi + +# Creating ro bind mounts +if [ -n "${BIND_RO}" ] +then + BINDS_RO="$(echo ${BIND_RO} | sed -e 's|;| |g')" + + for ENTRY in ${BINDS_RO} + do + DIRECTORY="$(echo ${ENTRY} | awk -F: '{ print $1 }')" + + mkdir -p "${DIRECTORY}" + done +fi + +# Creating overlay mounts +if [ -n "${CNT_OVERLAY}" ] +then + CNT_OVERLAYS="$(echo ${CNT_OVERLAY} | sed -e 's|;| |g')" + + for ENTRY in ${CNT_OVERLAYS} + do + DIRECTORY_LOWER="$(echo ${ENTRY} | awk -F: '{ print $1 }')" + DIRECTORY_UPPER="$(echo ${ENTRY} | awk -F: '{ print $2 }')" + DIRECTORY_WORK="$(echo ${ENTRY} | awk -F: '{ print $3 }')" + DIRECTORY_MERGED="$(echo ${ENTRY} | awk -F: '{ print $4 }')" + + for DIRECTORY in "${DIRECTORY_LOWER}" "${DIRECTORY_UPPER}" "${DIRECTORY_WORK}" "${DIRECTORY_MERGED}" + do + mkdir -p "${DIRECTORY}" + done + done +fi + +# config +mkdir -p "${CONFIG}" + +sed -e "s|@CNT_AUTO@|${CNT_AUTO}|g" \ + -e "s|@CNT_CONTAINER_SERVER@|${CNT_CONTAINER_SERVER}|g" \ + -e "s|@CNT_NETWORK_BRIDGE@|${CNT_NETWORK_BRIDGE}|g" \ + -e "s|@CNT_OVERLAY@|${CNT_OVERLAY}|g" \ + -e "s|@CNT_OVERLAY_OPTIONS@|${CNT_OVERLAY_OPTIONS}|g" \ + -e "s|@CNT_START@|${CNT_START}|g" \ + -e "s|@NAME@|${NAME}|g" \ + -e "s|@BIND@|${BIND}|g" \ + -e "s|@BIND_RO@|${BIND_RO}|g" \ + -e "s|@BOOT@|yes|g" \ + -e "s|@CAPABILITY@|${CAPABILITY}|g" \ + -e "s|@DIRECTORY@|${MACHINES}/${NAME}|g" \ + -e "s|@DROP_CAPABILITY@|${DROP_CAPABILITY}|g" \ + -e "s|@LINK_JOURNAL@|no|g" \ + -e "s|@MACHINE@|${NAME}|g" \ + -e "s|@NETWORK_VETH_EXTRA@|${NETWORK_VETH_EXTRA}|g" \ + -e "s|@PRIVATE_USERS@|no|g" \ + -e "s|@REGISTER@|yes|g" \ +"${CONFIG_TEMPLATE}" > "${CONFIG}/${NAME}.conf" + +# Run +"${SCRIPTS}/${SCRIPT}" $(echo "${@}" | sed -e 's| -- | |') + +# Post hooks +for FILE in "${HOOKS}/post-${COMMAND}".* "${HOOKS}/${NAME}.post-${COMMAND}" +do + if [ -x "${FILE}" ] + then + "${FILE}" + fi +done + +# done +echo "'${NAME}': container built." diff --git a/libexec/container/console b/libexec/container/console index ce53712..be2b897 100755 --- a/libexec/container/console +++ b/libexec/container/console @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (C) 2014-2021 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> # # SPDX-License-Identifier: GPL-3.0+ # @@ -66,6 +66,9 @@ Parameters () Usage () { echo "Usage: ${PROGRAM} ${COMMAND} -n|--name NAME" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + exit 1 } diff --git a/libexec/container/enter b/libexec/container/enter index 2664fdc..b366ba6 100755 --- a/libexec/container/enter +++ b/libexec/container/enter @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (C) 2014-2021 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> # # SPDX-License-Identifier: GPL-3.0+ # @@ -66,6 +66,9 @@ Parameters () Usage () { echo "Usage: ${PROGRAM} ${COMMAND} -n|--name NAME" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + exit 1 } @@ -108,7 +111,7 @@ done SSH_CLIENT="${SSH_CLIENT:-127.0.0.1 0 0}" # Run -nsenter --all --target "${LEADER}" --wd="${MACHINES}/${NAME}/root" /usr/bin/script -c "LC_ALL=C.UTF-8 /bin/bash -l" -q /dev/null +nsenter --all --target "${LEADER}" --wd="${MACHINES}/${NAME}/root" /usr/bin/script -c "LC_ALL=C.UTF-8 SSH_CLIENT=\"${SSH_CLIENT}\" /bin/bash -l" -q /dev/null case "${SSH_CLIENT}" in 127.0.0.1*) diff --git a/libexec/container/create b/libexec/container/get index 418bc35..0d0f420 100755 --- a/libexec/container/create +++ b/libexec/container/get @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (C) 2014-2021 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> # # SPDX-License-Identifier: GPL-3.0+ # @@ -27,12 +27,12 @@ COMMAND="$(basename ${0})" CONFIG="/etc/${SOFTWARE}/config" HOOKS="/etc/${SOFTWARE}/hooks" MACHINES="/var/lib/machines" -SCRIPTS="/usr/share/${SOFTWARE}/scripts" +SCRIPTS="/usr/share/${SOFTWARE}/get-scripts" CONFIG_TEMPLATE="/usr/share/${SOFTWARE}/config/container.conf.in" Parameters () { - GETOPT_LONGOPTIONS="name:,cnt.container-server:,cnt.overlay:,cnt.overlay-options:,bind:,bind-ro:,capability:,drop-capability:,script:,verbose," + GETOPT_LONGOPTIONS="name:,cnt.container-server:,cnt.overlay:,cnt.overlay-options:,start:,bind:,bind-ro:,capability:,drop-capability:,script:,verbose," GETOPT_OPTIONS="n:,b:,c:,d:,s:,v," PARAMETERS="$(getopt --longoptions ${GETOPT_LONGOPTIONS} --name=${COMMAND} --options ${GETOPT_OPTIONS} --shell sh -- ${@})" @@ -73,6 +73,11 @@ Parameters () shift 2 ;; + --cnt.start) + CNT_START="${2}" + shift 2 + ;; + -b|--bind) BIND="${2}" shift 2 @@ -118,7 +123,10 @@ Parameters () Usage () { - echo "Usage: ${PROGRAM} ${COMMAND} -n|--name NAME [--cnt.container-server=true|false|FQDN] [--cnt.overlay=DIRECTORY_LOWER:DIRECTORY_UPPER:DIRECTORY_WORK:DIRECTORY_MERGED] [--cnt.overlay-options=OPTION[,OPTION]] [-b|--bind DIRECTORY:DIRECTORY[:OPTIONS]] [--bind-ro DIRECTORY:DIRECTORY[:OPTIONS]] [-c|--capability CAPABILITY[,CAPABILITY]] [-d|--drop-capability DROP_CAPABILITY[,DROP_CAPABILITY]] [-s|--script SCRIPT] [-v|--verbose] [-- SCRIPT_OPTIONS]" >&2 + echo "Usage: ${PROGRAM} ${COMMAND} -n|--name NAME [--cnt.container-server=true|false|FQDN] [--cnt.overlay=DIRECTORY_LOWER:DIRECTORY_UPPER:DIRECTORY_WORK:DIRECTORY_MERGED] [--cnt.overlay-options=OPTION[,OPTION]] [--cnt.start=OPTION[,OPTION]] [-b|--bind DIRECTORY:DIRECTORY[:OPTIONS]] [--bind-ro DIRECTORY:DIRECTORY[:OPTIONS]] [-c|--capability CAPABILITY[,CAPABILITY]] [-d|--drop-capability DROP_CAPABILITY[,DROP_CAPABILITY]] [-s|--script SCRIPT] [-v|--verbose] [-- SCRIPT_OPTIONS]" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + exit 1 } @@ -149,8 +157,8 @@ then TARGET="$(basename $(readlink ${SCRIPTS}/default))" case "${TARGET}" in - container_script) - TARGET="$(basename $(readlink /etc/alternatives/container_script))" + container_get-script) + TARGET="$(basename $(readlink /etc/alternatives/container_get-script))" ;; esac @@ -162,7 +170,7 @@ then exit 1 fi else - SCRIPT="debian" + SCRIPT="curl" fi else if [ ! -e "${SCRIPTS}/${SCRIPT}" ] @@ -177,7 +185,7 @@ case "${VERBOSE}" in cat << EOF ################################################################################ -Creating container: ${NAME} +Building container: ${NAME} ################################################################################ EOF @@ -248,6 +256,7 @@ sed -e "s|@CNT_AUTO@|${CNT_AUTO}|g" \ -e "s|@CNT_NETWORK_BRIDGE@|${CNT_NETWORK_BRIDGE}|g" \ -e "s|@CNT_OVERLAY@|${CNT_OVERLAY}|g" \ -e "s|@CNT_OVERLAY_OPTIONS@|${CNT_OVERLAY_OPTIONS}|g" \ + -e "s|@CNT_START@|${CNT_START}|g" \ -e "s|@NAME@|${NAME}|g" \ -e "s|@BIND@|${BIND}|g" \ -e "s|@BIND_RO@|${BIND_RO}|g" \ diff --git a/libexec/container/info b/libexec/container/info new file mode 100755 index 0000000..b713e7a --- /dev/null +++ b/libexec/container/info @@ -0,0 +1,221 @@ +#!/bin/sh + +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# +# SPDX-License-Identifier: GPL-3.0+ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +set -e + +PROJECT="open-infrastructure" +SOFTWARE="compute-tools" +PROGRAM="container" +COMMAND="$(basename ${0})" + +CONFIG="/etc/${SOFTWARE}/config" +HOOKS="/etc/${SOFTWARE}/hooks" +MACHINES="/var/lib/machines" + +VERSION="$(${PROGRAM} version)" + +Parameters () +{ + GETOPT_LONGOPTIONS="name:,status,os,ip," + GETOPT_OPTIONS="n:," + + PARAMETERS="$(getopt --longoptions ${GETOPT_LONGOPTIONS} --name=${COMMAND} --options ${GETOPT_OPTIONS} --shell sh -- ${@})" + + if [ "${?}" != "0" ] + then + echo "'${COMMAND}': getopt exit" >&2 + exit 1 + fi + + eval set -- "${PARAMETERS}" + + while true + do + case "${1}" in + -n|--name) + NAME="${2}" + shift 2 + ;; + + --status) + ACTIONS="${ACTIONS} status" + shift 1 + ;; + + --os) + ACTIONS="${ACTIONS} os" + shift 1 + ;; + + --ip) + ACTIONS="${ACTIONS} ip" + shift 1 + ;; + + --) + shift 1 + break + ;; + + *) + echo "'${COMMAND}': getopt error" >&2 + exit 1 + ;; + esac + done +} + +Usage () +{ + echo "Usage: ${PROGRAM} ${COMMAND} -n|--name NAME [--status] [--os] [--ip]" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + + exit 1 +} + +Parameters "${@}" + +# Pre hooks +for FILE in "${HOOKS}/pre-${COMMAND}".* "${HOOKS}/${NAME}.pre-${COMMAND}" +do + if [ -x "${FILE}" ] + then + "${FILE}" + fi +done + +ACTIONS="${ACTIONS:-status os ip}" +HOST="$(cat /etc/hostname)" + +# Run + +# Status +STATUS="$(machinectl show ${NAME} 2>&1 | awk -FState= '/^State=/ { print $2 }')" + +if [ -e "${CONFIG}/${NAME}.conf" ] +then + CONTAINER_SERVER="$(awk -Fcnt.container-server= '/^cnt.container-server=/ { print $2 }' ${CONFIG}/${NAME}.conf)" + CONTAINER_SERVER="${CONTAINER_SERVER:-false}" + + case "${CONTAINER_SERVER}" in + ${HOST}|true) + ;; + + *) + STATUS="other" + ;; + esac +else + STATUS="other" +fi + +case "${STATUS}" in + running) + STATUS="started" + ;; + + other) + ;; + + *) + STATUS="stopped" + ;; +esac + +# OS +VERSION_BASH="$(chroot ${MACHINES}/${NAME} apt-cache policy bash | awk '/Installed: / { print $2 }')" + +case "${VERSION_BASH}" in + 4.1-*|4.1.[0-9]*) + OS="Debian 6 (squeeze)" + ;; + + 4.2-*|4.2.[0-9]*) + OS="Debian 7 (wheezy)" + ;; + + 4.3-*|4.3.[0-9]*) + OS="Debian 8 (jessie)" + ;; + + 4.4-*|4.4.[0-9]*) + OS="Debian 9 (stretch)" + ;; + + 5.0-*|5.0.[0-9]*) + OS="Debian 10 (buster)" + ;; + + 5.1-*|5.1.[0-9]*) + OS="Debian 11 (bullseye)" + ;; + + 5.2-*|5.2.[0-9]*) + OS="Debian 12 (bookworm)" + ;; + + *) + OS="n/a" + ;; +esac + +case "${STATUS}" in + started) + IP="$(cnt run -n ${NAME} -- hostname -I)" + ;; + + *) + if ls "${MACHINES}/${NAME}/etc/systemd/network"/*.network > /dev/null 2>&1 + then + IP="$(awk -FAddress= '/^Address/ { printf "%s ", $2 }' ${MACHINES}/${NAME}/etc/systemd/network/*.network)" + elif [ -e "${MACHINES}/${NAME}/etc/network/interfaces" ] + then + IP="$(awk '/address/ { printf "%s ", $2 }' ${MACHINES}/${NAME}/etc/network/interfaces)" + fi + + IP="${IP:-n/a}" + ;; +esac + +for ACTION in ${ACTIONS} +do + case "${ACTION}" in + status) + echo "${STATUS}" + ;; + + os) + echo "${OS}" + ;; + + ip) + echo "${IP}" + ;; + esac +done + +# Post hooks +for FILE in "${HOOKS}/post-${COMMAND}".* "${HOOKS}/${NAME}.post-${COMMAND}" +do + if [ -x "${FILE}" ] + then + "${FILE}" + fi +done diff --git a/libexec/container/key b/libexec/container/key index 5f76fb2..efd214e 100755 --- a/libexec/container/key +++ b/libexec/container/key @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (C) 2014-2021 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> # # SPDX-License-Identifier: GPL-3.0+ # @@ -76,7 +76,10 @@ Parameters () Usage () { - echo "Usage: ${PROGRAM} ${COMMAND} [-a|--add KEY] [-l|--list] [-r|--remove KEY]" >&2 + echo "Usage: ${PROGRAM} ${COMMAND} [-a|--add KEY_FILE|KEY_ID] [-l|--list] [-r|--remove KEY|KEY_ID]" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + exit 1 } @@ -87,6 +90,15 @@ then Usage fi +if [ ! -w "${KEYS}" ] +then + if [ "$(id -u)" -ne 0 ] + then + echo "'${COMMAND}': need root privileges (or write permissions to '${KEYS}')" >&2 + exit 1 + fi +fi + # Pre hooks for FILE in "${HOOKS}/pre-${COMMAND}".* "${HOOKS}/${NAME}.pre-${COMMAND}" do @@ -105,7 +117,7 @@ then chmod 0700 "${KEYS}" cat > "${KEYS}/gnupg.conf" << EOF -keyserver hkps://hkps.pool.sks-keyservers.net +keyserver hkps://keys.openpgp.org keyserver-options include-revoked keyserver-options no-honor-keyserver-url @@ -131,7 +143,18 @@ fi case "${ACTION}" in add) - gpg --homedir "${KEYS}" --import "${ADD}" + if [ -e "${ADD}" ] + then + gpg --homedir "${KEYS}" --import "${ADD}" + elif [ -e "/usr/share/${SOFTWARE}/keys/${ADD}" ] + then + gpg --homedir "${KEYS}" --import "/usr/share/${SOFTWARE}/keys/${ADD}" + elif [ -e "/usr/share/${SOFTWARE}/keys/${ADD}.pub" ] + then + gpg --homedir "${KEYS}" --import "/usr/share/${SOFTWARE}/keys/${ADD}.pub" + else + gpg --homedir "${KEYS}" --recv "${ADD}" + fi ;; list) diff --git a/libexec/container/limit b/libexec/container/limit index 6323a42..b7f6e9b 100755 --- a/libexec/container/limit +++ b/libexec/container/limit @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (C) 2014-2021 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> # # SPDX-License-Identifier: GPL-3.0+ # @@ -106,6 +106,9 @@ Parameters () Usage () { echo "Usage: ${PROGRAM} ${COMMAND} -n|--name NAME [--blockio-device-weight \"DEVICE WEIGHT\"] [--blockio-read-bandwidth \"DEVICE BYTES\"] [-b|--blockio-weight WEIGHT] [--blockio-write-bandwidth \"DEVICE BYTES\"] [-c|--cpu-quota QUOTA] [--cpu-shares SHARES] [-m|--memory-limit BYTES] [-t|--tasks-max NUMBER]" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + exit 1 } diff --git a/libexec/container/list b/libexec/container/list index 30446c0..a56c1f8 100755 --- a/libexec/container/list +++ b/libexec/container/list @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (C) 2014-2021 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> # # SPDX-License-Identifier: GPL-3.0+ # @@ -109,6 +109,9 @@ Parameters () Usage () { echo "Usage: ${PROGRAM} ${COMMAND} [-a|--all] [--csv-separator SEPARATOR] [--format FORMAT] [-h|--host HOSTNAME] [--nwdiag-color COLOR] [--nwdiag-label LABEL] [-o|--other] [-s|--started] [-t|--stopped]" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + exit 1 } @@ -366,15 +369,33 @@ do ADDRESS="" - if ls "${MACHINES}/${CONTAINER}/etc/systemd/network"/*.network > /dev/null 2>&1 - then - ADDRESS="$(awk -FAddress= '/^Address/ { print $2 }' ${MACHINES}/${CONTAINER}/etc/systemd/network/*.network | head -n1)" - elif [ -e "${MACHINES}/${CONTAINER}/etc/network/interfaces" ] - then - ADDRESS="$(awk '/address/ { print $2 }' ${MACHINES}/${CONTAINER}/etc/network/interfaces | head -n1)" - fi + case "${STATE}" in + started) + case "${FORMAT}" in + shell|sh) + ;; + + *) + LEADER="$(machinectl status ${CONTAINER} | awk '/Leader: / { print $2 }')" + ADDRESS="$(nsenter --all --target "${LEADER}" /bin/hostname -I)" + ;; + esac + + ADDRESS="${ADDRESS:-none}" + ;; - ADDRESS="${ADDRESS:-n/a}" + *) + if ls "${MACHINES}/${CONTAINER}/etc/systemd/network"/*.network > /dev/null 2>&1 + then + ADDRESS="$(for IP in $(awk -FAddress= '/^Address/ { print $2 }' ${MACHINES}/${CONTAINER}/etc/systemd/network/*.network); do echo -n "${IP} "; done)" + elif [ -e "${MACHINES}/${CONTAINER}/etc/network/interfaces" ] + then + ADDRESS="$(for IP in $(awk '/address/ { print $2 }' ${MACHINES}/${CONTAINER}/etc/network/interfaces); do echo -n "${IP} "; done)" + fi + + ADDRESS="${ADDRESS:-n/a}" + ;; + esac if echo ${LIST} | grep -qs all then diff --git a/libexec/container/log b/libexec/container/log index e514391..b7a000d 100755 --- a/libexec/container/log +++ b/libexec/container/log @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (C) 2014-2021 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> # # SPDX-License-Identifier: GPL-3.0+ # @@ -76,6 +76,9 @@ Parameters () Usage () { echo "Usage: ${PROGRAM} ${COMMAND} [-n|--name NAME] [-d|--date DATE|today|today-N|yesterday] [-u|--user USER]" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + exit 1 } diff --git a/libexec/container/move b/libexec/container/move index a76cde1..fdc19e6 100755 --- a/libexec/container/move +++ b/libexec/container/move @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (C) 2014-2021 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> # # SPDX-License-Identifier: GPL-3.0+ # @@ -77,6 +77,9 @@ Parameters () Usage () { echo "Usage: ${PROGRAM} ${COMMAND} [-f|--force] -n|--new NAME -o|--old NAME" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + exit 1 } diff --git a/libexec/container/rebuild b/libexec/container/rebuild new file mode 100755 index 0000000..e526520 --- /dev/null +++ b/libexec/container/rebuild @@ -0,0 +1,152 @@ +#!/bin/sh + +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# +# SPDX-License-Identifier: GPL-3.0+ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +set -e + +PROJECT="open-infrastructure" +SOFTWARE="compute-tools" +PROGRAM="container" +COMMAND="$(basename ${0})" + +HOOKS="/etc/${SOFTWARE}/hooks" +MACHINES="/var/lib/machines" + +Parameters () +{ + OPTIONS_ALL="" + + GETOPT_LONGOPTIONS="name:,force,verbose," + GETOPT_OPTIONS="n:,f,v," + + PARAMETERS="$(getopt --longoptions ${GETOPT_LONGOPTIONS} --name=${COMMAND} --options ${GETOPT_OPTIONS} --shell sh -- ${@})" + + if [ "${?}" != "0" ] + then + echo "'${COMMAND}': getopt exit" >&2 + exit 1 + fi + + eval set -- "${PARAMETERS}" + + while true + do + case "${1}" in + -n|--name) + NAME="${2}" + shift 2 + ;; + + -f|--force) + FORCE="true" + shift 1 + + OPTIONS_ALL="${OPTIONS_ALL} --force" + ;; + + -v|--verbose) + VERBOSE="true" + shift 1 + + OPTIONS_ALL="${OPTIONS_ALL} --verbose" + ;; + + --) + shift 1 + break + ;; + + *) + echo "'${COMMAND}': getopt error" >&2 + exit 1 + ;; + esac + done +} + +Usage () +{ + echo "Usage: ${PROGRAM} ${COMMAND} -n|--name NAME [-f|--force] [-v|--verbose]" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + + exit 1 +} + +Parameters "${@}" + +if [ -z "${NAME}" ] +then + Usage +fi + +case "${NAME}" in + ALL) + NAMES="$(${PROGRAM} list --format shell --started)" + + for NAME in ${NAMES} + do + ${PROGRAM} rebuild,start --name ${NAME} ${OPTIONS_ALL} || true + done + + exit 0 + ;; +esac + +if [ ! -e "${MACHINES}/${NAME}" ] +then + echo "'${NAME}': no such container" >&2 + exit 1 +fi + +# Pre hooks +for FILE in "${HOOKS}/pre-${COMMAND}".* "${HOOKS}/${NAME}.pre-${COMMAND}" +do + if [ -x "${FILE}" ] + then + "${FILE}" + fi +done + +# Run +case "${VERBOSE}" in + true) + echo -n "Rebuilding container ${NAME}..." + ;; +esac + +${PROGRAM} stop ${OPTIONS_ALL} --name ${NAME} || true +sleep 0.5 +${PROGRAM} remove ${OPTIONS_ALL} --name ${NAME} || true +sleep 0.5 +${PROGRAM} build --name ${NAME} || true + +case "${VERBOSE}" in + true) + echo " done." + ;; +esac + +# Post hooks +for FILE in "${HOOKS}/post-${COMMAND}".* "${HOOKS}/${NAME}.post-${COMMAND}" +do + if [ -x "${FILE}" ] + then + "${FILE}" + fi +done diff --git a/libexec/container/remove b/libexec/container/remove index 3205c32..4cb5d48 100755 --- a/libexec/container/remove +++ b/libexec/container/remove @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (C) 2014-2021 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> # # SPDX-License-Identifier: GPL-3.0+ # @@ -90,6 +90,9 @@ Parameters () Usage () { echo "Usage: ${PROGRAM} ${COMMAND} -n|--name NAME [--allow-stop] [-f|--force] [-v|--verbose]" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + exit 1 } @@ -147,7 +150,7 @@ case "${STATE}" in case "${ALLOW_STOP}" in true) echo "'${NAME}': container is started, stopping it now" >&2 - ${PROGRAM} stop -n ${NAME} + ${PROGRAM} stop -n ${NAME} -f ;; *) diff --git a/libexec/container/restart b/libexec/container/restart index 922629d..0eb753c 100755 --- a/libexec/container/restart +++ b/libexec/container/restart @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (C) 2014-2021 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> # # SPDX-License-Identifier: GPL-3.0+ # @@ -31,8 +31,8 @@ Parameters () { OPTIONS_ALL="" - GETOPT_LONGOPTIONS="name:,verbose," - GETOPT_OPTIONS="n:,v," + GETOPT_LONGOPTIONS="name:,force,interactive,verbose," + GETOPT_OPTIONS="n:,f,i,v," PARAMETERS="$(getopt --longoptions ${GETOPT_LONGOPTIONS} --name=${COMMAND} --options ${GETOPT_OPTIONS} --shell sh -- ${@})" @@ -52,6 +52,20 @@ Parameters () shift 2 ;; + -f|--force) + FORCE="true" + shift 1 + + OPTIONS_ALL="${OPTIONS_ALL} --force" + ;; + + -i|--interactive) + INTERACTIVE="true" + shift 1 + + OPTIONS_ALL="${OPTIONS_ALL} --interactive" + ;; + -v|--verbose) VERBOSE="true" shift 1 @@ -74,7 +88,10 @@ Parameters () Usage () { - echo "Usage: ${PROGRAM} ${COMMAND} -n|--name NAME [-v|--verbose]" >&2 + echo "Usage: ${PROGRAM} ${COMMAND} -n|--name NAME [-f|--force] [-i|--interactive] [-v|--verbose]" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + exit 1 } @@ -104,6 +121,23 @@ then exit 1 fi +if [ "${FORCE}" != "true" ] || [ "${INTERACTIVE}" = "true" ] +then + echo -n "'${NAME}': restart container '${NAME}' [y|N]? " + read STOP + + STOP="$(echo ${STOP} | tr '[A-Z]' '[a-z]')" + + case "${STOP}" in + y|yes) + ;; + + *) + exit 1 + ;; + esac +fi + # Pre hooks for FILE in "${HOOKS}/pre-${COMMAND}".* "${HOOKS}/${NAME}.pre-${COMMAND}" do diff --git a/libexec/container/run b/libexec/container/run index bf8d0a7..4daeaa2 100755 --- a/libexec/container/run +++ b/libexec/container/run @@ -66,6 +66,9 @@ Parameters () Usage () { echo "Usage: ${PROGRAM} ${COMMAND} -n|--name NAME -- COMMAND" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + exit 1 } diff --git a/libexec/container/start b/libexec/container/start index 089aa7d..1f22325 100755 --- a/libexec/container/start +++ b/libexec/container/start @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (C) 2014-2021 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> # # SPDX-License-Identifier: GPL-3.0+ # @@ -99,6 +99,9 @@ Parameters () Usage () { echo "Usage: ${PROGRAM} ${COMMAND} -n|--name NAME [-f|--force]" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + exit 1 } @@ -131,6 +134,12 @@ then exit 1 fi +# options +if grep -Eqs "^ *cnt.start=" "${CONFIG}/${NAME}.conf" | grep -qs force +then + FORCE="true" +fi + case "${START}" in false) STATE="$(machinectl show ${NAME} 2>&1 | awk -FState= '/^State=/ { print $2 }')" @@ -182,6 +191,13 @@ case "${HOST_ARCHITECTURE}" in ;; esac +if systemctl status systemd-networkd > /dev/null 2>&1 +then + NETWORK_SUBSYSTEM="systemd-networkd" +else + NETWORK_SUBSYSTEM="ifupdown" +fi + case "${START}" in start) ;; @@ -353,7 +369,7 @@ then NETWORK_VETH_EXTRA="${NETWORK_VETH_EXTRA} --network-veth-extra=${VETH}" INTERFACE="$(echo ${VETH} | awk -F: '{ print $1 }')" - if [ "$(echo ${INTERFACE} | wc -c)" -gt 15 ] + if [ "$(echo ${INTERFACE} | wc -c)" -gt 16 ] then echo "'${INTERFACE}': name exceeds maximum of 15 characters, network might be not working." fi @@ -373,7 +389,7 @@ then INTERFACE="$(echo ${BRIDGE_DEFINITION} | awk -F: '{ print $1 }')" BRIDGE="$(echo ${BRIDGE_DEFINITION} | awk -F: '{ print $2 }')" - if [ "$(echo ${INTERFACE} | wc -c)" -gt 15 ] + if [ "$(echo ${INTERFACE} | wc -c)" -gt 16 ] then echo "'${INTERFACE}': name exceeds maximum of 15 characters, network might be not working." fi @@ -381,6 +397,9 @@ then if [ -n "${BRIDGE}" ] && [ -n "${INTERFACE}" ] then + case "${NETWORK_SUBSYSTEM}" in + ifupdown) + cat > "/etc/network/interfaces.d/${INTERFACE}" << EOF allow-hotplug ${INTERFACE} iface ${INTERFACE} inet manual @@ -390,6 +409,22 @@ iface ${INTERFACE} inet manual post-down ip link set ${INTERFACE} down EOF + ;; + + systemd-networkd) + mkdir -p /run/systemd/network + +cat > "/run/systemd/network/${INTERFACE}.network" << EOF +[Match] +Name=${INTERFACE} + +[Network] +Bridge=${BRIDGE} +EOF + + networkctl reload + ;; + esac else echo "Warning bridge definition '${BRIDGE_DEFINITION}' not recognized (expected <bridge>:<interface>): Ignoring" fi @@ -521,6 +556,9 @@ case "${START}" in ;; esac + mkdir -p "/var/lib/${SOFTWARE}/state" + echo "start" > "/var/lib/${SOFTWARE}/state/${NAME}.run" + ${SETARCH} systemd-nspawn --keep-unit ${BIND} ${BIND_RO} ${BOOT} ${CAPABILITY} ${DIRECTORY} ${DROP_CAPABILITY} ${MACHINE} ${NETWORK_VETH_EXTRA} ${LINK_JOURNAL} ${REGISTER} case "${VERBOSE}" in diff --git a/libexec/container/status b/libexec/container/status index 9c3a076..5b930a7 100755 --- a/libexec/container/status +++ b/libexec/container/status @@ -67,6 +67,9 @@ Parameters () Usage () { echo "Usage: ${PROGRAM} ${COMMAND} -n|--name NAME" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + exit 1 } diff --git a/libexec/container/stop b/libexec/container/stop index 58fc0e9..8ca98ce 100755 --- a/libexec/container/stop +++ b/libexec/container/stop @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (C) 2014-2021 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> # # SPDX-License-Identifier: GPL-3.0+ # @@ -34,8 +34,8 @@ Parameters () { OPTIONS_ALL="" - GETOPT_LONGOPTIONS="name:,force,clean,verbose," - GETOPT_OPTIONS="n:,f,v," + GETOPT_LONGOPTIONS="name:,force,interactive,kill,clean,stateless,verbose," + GETOPT_OPTIONS="n:,f,i,k,v," PARAMETERS="$(getopt --longoptions ${GETOPT_LONGOPTIONS} --name=${COMMAND} --options ${GETOPT_OPTIONS} --shell sh -- ${@})" @@ -62,6 +62,20 @@ Parameters () OPTIONS_ALL="${OPTIONS_ALL} --force" ;; + -i|--interactive) + INTERACTIVE="true" + shift 1 + + OPTIONS_ALL="${OPTIONS_ALL} --interactive" + ;; + + -k|--kill) + KILL="true" + shift 1 + + OPTIONS_ALL="${OPTIONS_ALL} --kill" + ;; + --clean) # internal option CLEAN="true" @@ -70,6 +84,14 @@ Parameters () OPTONS_ALL="${OPTIONS_ALL} --clean" ;; + --stateless) + # internal option + STATELESS="true" + shift 1 + + OPTIONS_ALL="${OPTIONS_ALL} --stateless" + ;; + -v|--verbose) VERBOSE="true" shift 1 @@ -92,7 +114,10 @@ Parameters () Usage () { - echo "Usage: ${PROGRAM} ${COMMAND} -n|--name NAME [-f|--force] [-v|--verbose]" >&2 + echo "Usage: ${PROGRAM} ${COMMAND} -n|--name NAME [-f|--force] [-i|--interactive] [-v|--verbose]" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + exit 1 } @@ -143,6 +168,13 @@ then exit 1 fi +if systemctl status systemd-networkd > /dev/null 2>&1 +then + NETWORK_SUBSYSTEM="systemd-networkd" +else + NETWORK_SUBSYSTEM="ifupdown" +fi + # Pre hooks for FILE in "${HOOKS}/pre-${COMMAND}".* "${HOOKS}/${NAME}.pre-${COMMAND}" do @@ -217,7 +249,16 @@ case "${CLEAN}" in for VETH in ${VETHS} do INTERFACE="$(echo ${VETH} | awk -F: '{ print $1 }')" - FILE="/etc/network/interfaces.d/${INTERFACE}" + + case "${NETWORK_SUBSYSTEM}" in + ifupdown) + FILE="/etc/network/interfaces.d/${INTERFACE}" + ;; + + systemd-networkd) + FILE="/run/systemd/network/${INTERFACE}.network" + ;; + esac if [ -f "${FILE}" ] then @@ -244,7 +285,7 @@ case "${STATE}" in ;; esac -case "${FORCE}" in +case "${KILL}" in true) MODE="terminate" ;; @@ -254,6 +295,23 @@ case "${FORCE}" in ;; esac +if [ "${FORCE}" != "true" ] || [ "${INTERACTIVE}" = "true" ] +then + echo -n "'${NAME}': stop container '${NAME}' [y|N]? " + read STOP + + STOP="$(echo ${STOP} | tr '[A-Z]' '[a-z]')" + + case "${STOP}" in + y|yes) + ;; + + *) + exit 1 + ;; + esac +fi + # Run case "${VERBOSE}" in true) @@ -263,7 +321,7 @@ esac machinectl ${MODE} ${NAME} -case "${FORCE}" in +case "${KILL}" in true) VETHS="$(awk -Fnetwork-veth-extra= '/^network-veth-extra=/ { print $2 }' ${CONFIG}/${NAME}.conf | awk -F: '{ print $1 }')" @@ -271,6 +329,18 @@ case "${FORCE}" in do ip link delete ${VETH} > /dev/null 2>&1 || true done + + rm -f "${MACHINES}/.#${NAME}.lck" + ;; +esac + +case "${STATELESS}" in + true) + ;; + + *) + mkdir -p "/var/lib/${SOFTWARE}/state" + echo "stop" > "/var/lib/${SOFTWARE}/state/${NAME}.run" ;; esac diff --git a/libexec/container/top b/libexec/container/top index c846000..268da9a 100755 --- a/libexec/container/top +++ b/libexec/container/top @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (C) 2014-2021 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> # # SPDX-License-Identifier: GPL-3.0+ # @@ -63,6 +63,9 @@ Parameters () Usage () { echo "Usage: ${PROGRAM} ${COMMAND} [-d|--delay DELAY]" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + exit 1 } diff --git a/libexec/container/update b/libexec/container/update new file mode 100755 index 0000000..e2d9c80 --- /dev/null +++ b/libexec/container/update @@ -0,0 +1,270 @@ +#!/bin/sh + +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# +# SPDX-License-Identifier: GPL-3.0+ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +set -e + +PROJECT="open-infrastructure" +SOFTWARE="compute-tools" +PROGRAM="container" +COMMAND="$(basename ${0})" + +HOOKS="/etc/${SOFTWARE}/hooks" + +Parameters () +{ + GETOPT_LONGOPTIONS="name:,full-upgrade,interactive,autoremove,purge,yes," + GETOPT_OPTIONS="n:,f,i,r,p,y," + + PARAMETERS="$(getopt --longoptions ${GETOPT_LONGOPTIONS} --name=${COMMAND} --options ${GETOPT_OPTIONS} --shell sh -- ${@})" + + if [ "${?}" != "0" ] + then + echo "'${COMMAND}': getopt exit" >&2 + exit 1 + fi + + eval set -- "${PARAMETERS}" + + while true + do + case "${1}" in + -n|--name) + NAME="${2}" + shift 2 + ;; + + -f|--full-upgrade) + FULL_UPGRADE="true" + shift 1 + ;; + + -i|--interactive) + INTERACTIVE="true" + shift 1 + ;; + + -r|--autoremove) + AUTOREMOVE="true" + shift 1 + ;; + + -p|--purge) + PURGE="--purge" + shift 1 + ;; + + -y|--yes) + YES="-y" + shift 1 + ;; + + --) + shift 1 + break + ;; + + *) + echo "'${COMMAND}': getopt error" >&2 + exit 1 + ;; + esac + done +} + +Usage () +{ + echo "Usage: ${PROGRAM} ${COMMAND} -n|--name NAME [-f|--full-upgrade] [-i|--interactive] [-r|--autoremove] [-p|--purge] [-y|--yes]" >&2 + echo + echo "See ${COMMAND}(1), ${PROGRAM}(1) and ${PROJECT}(7) for more information." + + exit 1 +} + +Parameters "${@}" + +if [ -z "${NAME}" ] +then + Usage +fi + +Notification () +{ + TYPE="${1}" + NUMBER="${2}" + PACKAGES="${3}" + + if [ -z "${PACKAGES}" ] + then + return + fi + + CONTAINER_USER="${SUDO_USER:-${USER}}" + + DATE="$(date +%Y-%m-%d\ %H:%M:%S)" + HOST="$(hostname -f 2> /dev/null || hostname)" + + # logfile + echo "${DATE} ${HOST} ${CONTAINER_USER} ${NAME} ${NUMBER} ${TYPE}: ${PACKAGES}" >> "/var/log/${SOFTWARE}/${PROGRAM}.log" + + # irc + if [ -e /usr/bin/irk ] + then + for FILE in "/etc/${SOFTWARE}/${PROGRAM}.conf" "/etc/${SOFTWARE}/${PROGRAM}.conf.d"/*.conf + do + if [ -e "${FILE}" ] + then + . "${FILE}" + fi + done + + if [ -n "${IRK_TARGETS}" ] + then + for TARGET in ${IRK_TARGETS} + do + irk "${TARGET}" "\x0300${CONTAINER_USER}\x03@\x0312${HOST}:\x03 \x0303${NAME}\x03 \x0307${NUMBER} ${TYPE}\x03: ${PACKAGES}" + done + fi + fi +} + +case "${NAME}" in + ALL) + NAMES="$(container list --started --format shell)" + ;; + + *) + NAMES="${NAME}" + ;; +esac + +# Pre hooks +for FILE in "${HOOKS}/pre-${COMMAND}".* "${HOOKS}/${NAME}.pre-${COMMAND}" +do + if [ -x "${FILE}" ] + then + "${FILE}" + fi +done + +if [ $(echo ${NAMES} | wc -w) -gt 1 ] +then + NAME_LOOP="true" +else + NAME_LOOP="false" +fi + +# Run +for NAME in ${NAMES} +do + case "${INTERACTIVE}" in + true) + case "${NAME_LOOP}" in + true) + echo + ;; + esac + + echo -n "'${NAME}': update container '${NAME}' [y|N|a]? " + read UPDATE + + UPDATE="$(echo ${UPDATE} | tr '[A-Z]' '[a-z]')" + + case "${UPDATE}" in + a|all) + INTERACTIVE="false" + ;; + + y|yes) + ;; + + *) + case "${NAME_LOOP}" in + true) + continue + ;; + + *) + exit 1 + ;; + esac + ;; + esac + ;; + esac + + echo "################################################################################" + echo "Updating ${NAME}" + echo "################################################################################" + + container run -n ${NAME} -- "apt update" + + UPDATE_NUMBER="$(container run -n ${NAME} -- "apt \-\-simulate full-upgrade" | awk '/^[0-9]* upgraded, / { print $1 }')" + + case "${UPDATE_NUMBER}" in + 0) + ;; + + *) + # usefull use of grep to de-colorize apt output + UPDATE_PACKAGES="$(for PACKAGE in $(container run -n ${NAME} -- "apt list \-\-upgradable 2>/dev/null | grep '\/'" | awk -F/ '{ print $1 }'); do echo -n "${PACKAGE} "; done | sed -e 's| $||'; echo)" + + case "${FULL_UPGRADE}" in + true) + container run -n ${NAME} -- "DEBCONF_FRONTEND='noninteractive' DEBCONF_PRIORITY='critical' DEBCONF_NONINTERACTIVE_SEEN='true' DEBCONF_NOWARNINGS='true' apt \-o Dpkg::Options::=\-\-force-confold -f ${YES} full-upgrade" + ;; + + *) + container run -n ${NAME} -- "DEBCONF_FRONTEND='noninteractive' DEBCONF_PRIORITY='critical' DEBCONF_NONINTERACTIVE_SEEN='true' DEBCONF_NOWARNINGS='true' apt \-o Dpkg::Options::=\-\-force-confold -f ${YES} upgrade" + ;; + esac + + Notification "update(s)" "${UPDATE_NUMBER}" "${UPDATE_PACKAGES}" + ;; + esac + + case "${AUTOREMOVE}" in + true) + REMOVE_NUMBER="$(container run -n ${NAME} -- "apt \-\-simulate autoremove" | awk '/^[0-9]* upgraded, / { print $6 }')" + + case "${REMOVE_NUMBER}" in + 0) + ;; + + *) + REMOVE_PACKAGES="$(for LINE in $(container run -n ${NAME} -- "apt \-\-simulate autoremove" | grep '^ '); do echo ${LINE}; done | sed -e 's|^ ||' -e 's|
$||'; echo)" + + container run -n ${NAME} -- "apt ${YES} autoremove ${PURGE}" + + Notification "removal(s)" "${REMOVE_NUMBER}" "$(echo ${REMOVE_PACKAGES})" + ;; + esac + ;; + esac + + echo "'${NAME}': container updated." +done + +# Post hooks +for FILE in "${HOOKS}/post-${COMMAND}".* "${HOOKS}/${NAME}.post-${COMMAND}" +do + if [ -x "${FILE}" ] + then + "${FILE}" + fi +done diff --git a/libexec/container/version b/libexec/container/version index 3b33a4d..e580688 100755 --- a/libexec/container/version +++ b/libexec/container/version @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (C) 2014-2021 Daniel Baumann <daniel.baumann@open-infrastructure.net> +# Copyright (C) 2014-2022 Daniel Baumann <daniel.baumann@open-infrastructure.net> # # SPDX-License-Identifier: GPL-3.0+ # |