armbian-next: mountpoints.sh with all bind/volume directories definitions and looping func; use it to determine best bind/volume combination for each host OS in Docker

This commit is contained in:
Ricardo Pardini
2022-10-22 11:42:31 +02:00
parent 252f96967e
commit aaa59261bb
3 changed files with 114 additions and 42 deletions

View File

@@ -256,24 +256,6 @@ function docker_cli_build_dockerfile() {
}
function docker_cli_prepare_launch() {
# array for the generic armbian 'volumes' and their paths. Less specific first.
# @TODO: actually use this for smth.
declare -A -g DOCKER_ARMBIAN_VOLUMES=(
[".tmp"]="linux=anonymous darwin=anonymous" # tmpfs, discard, anonymous; whatever you wanna call it. It just needs to be 100% local to the container, and there's very little value in being able to look at it from the host.
["output"]="linux=bind darwin=bind" # catch-all output. specific subdirs are mounted below. it's a bind mount by default on both Linux and Darwin.
["output/images"]="linux=bind darwin=bind" # 99% of users want this as the result of their build, no matter if it's slow or not. bind on both.
["output/debs"]="linux=bind darwin=namedvolume" # generated output .deb files. not everyone is interested in this: most users just want images. Linux has fast binds, so bound by default. Darwin has slow binds, so it's a volume by default.
["output/logs"]="linux=bind darwin=bind" # log files produced. 100% of users want this. Bind on both Linux and Darwin. Is used to integrate launcher and actual-build logs, so must exist and work otherwise confusion ensues.
["cache"]="linux=bind darwin=namedvolume" # catch-all cache, could be bind-mounted or a volume. On Darwin it's too slow to bind-mount, so it's a volume by default. On Linux, it's a bind-mount by default.
["cache/gitballs"]="linux=bind darwin=namedvolume" # tarballs of git repos, can be bind-mounted or a volume. On Darwin it's too slow to bind-mount, so it's a volume by default. On Linux, it's a bind-mount by default.
["cache/toolchain"]="linux=bind darwin=namedvolume" # toolchain cache, can be bind-mounted or a volume. On Darwin it's too slow to bind-mount, so it's a volume by default. On Linux, it's a bind-mount by default.
["cache/aptcache"]="linux=bind darwin=namedvolume" # .deb apt cache, replaces apt-cacher-ng. Can be bind-mounted or a volume. On Darwin it's too slow to bind-mount, so it's a volume by default. On Linux, it's a bind-mount by default.
["cache/rootfs"]="linux=bind darwin=namedvolume" # rootfs .tar.zst cache, can be bind-mounted or a volume. On Darwin it's too slow to bind-mount, so it's a volume by default. On Linux, it's a bind-mount by default.
["cache/initrd"]="linux=bind darwin=namedvolume" # initrd.img cache, can be bind-mounted or a volume. On Darwin it's too slow to bind-mount, so it's a volume by default. On Linux, it's a bind-mount by default.
["cache/sources"]="linux=bind darwin=namedvolume" # operating directory. many things are cloned in here, and some are even built inside. needs to be local to the container, so it's a volume by default. On Linux, it's a bind-mount by default.
["cache/sources/linux-kernel"]="linux=bind darwin=namedvolume" # working tree for kernel builds. huge. contains both sources and the built object files. needs to be local to the container, so it's a volume by default. On Linux, it's a bind-mount by default.
)
display_alert "Preparing" "common Docker arguments" "debug"
declare -g -a DOCKER_ARGS=(
"--rm" # side effect - named volumes are considered not attached to anything and are removed on "docker volume prune", since container was removed.
@@ -283,38 +265,52 @@ function docker_cli_prepare_launch() {
"--cap-add=MKNOD" # (though MKNOD should be already present)
"--cap-add=SYS_PTRACE" # CAP_SYS_PTRACE is required for systemd-detect-virt in some cases @TODO: rpardini: so lets eliminate it
# "--mount" "type=bind,source=${SRC}/lib,target=${DOCKER_ARMBIAN_TARGET_PATH}/lib"
# type=volume, without source=, is an anonymous volume -- will be auto cleaned up together with the container;
# this could also be a type=tmpfs if you had enough ram - but armbian already does tmpfs for you if you
# have enough RAM (inside the container) so don't bother.
"--mount" "type=volume,destination=${DOCKER_ARMBIAN_TARGET_PATH}/.tmp"
# named volumes for different parts of the cache. so easy for user to drop any of them when needed
# @TODO: refactor this; this is only ideal for Darwin right now. Use DOCKER_ARMBIAN_VOLUMES to generate this.
"--mount" "type=volume,source=armbian-cache-parent,destination=${DOCKER_ARMBIAN_TARGET_PATH}/cache"
"--mount" "type=volume,source=armbian-cache-gitballs,destination=${DOCKER_ARMBIAN_TARGET_PATH}/cache/gitballs"
"--mount" "type=volume,source=armbian-cache-toolchain,destination=${DOCKER_ARMBIAN_TARGET_PATH}/cache/toolchain"
"--mount" "type=volume,source=armbian-cache-aptcache,destination=${DOCKER_ARMBIAN_TARGET_PATH}/cache/aptcache"
"--mount" "type=volume,source=armbian-cache-rootfs,destination=${DOCKER_ARMBIAN_TARGET_PATH}/cache/rootfs"
"--mount" "type=volume,source=armbian-cache-initrd,destination=${DOCKER_ARMBIAN_TARGET_PATH}/cache/initrd"
"--mount" "type=volume,source=armbian-cache-sources,destination=${DOCKER_ARMBIAN_TARGET_PATH}/cache/sources"
"--mount" "type=volume,source=armbian-cache-sources-linux-kernel,destination=${DOCKER_ARMBIAN_TARGET_PATH}/cache/sources/linux-kernel"
# Pass env var ARMBIAN_RUNNING_IN_CONTAINER to indicate we're running under Docker. This is also set in the Dockerfile; make sure.
"--env" "ARMBIAN_RUNNING_IN_CONTAINER=yes"
# @TODO: if user on terminal, pass the TERM down to the container.
"--env" "TERM=${TERM}"
# @TODO: pass down the CI-related env vars.
)
# @TODO: auto-compute this list; just get the dirs and filter some out
for MOUNT_DIR in "lib" "config" "extensions" "packages" "patch" "tools" "userpatches" "output"; do
# This will receive the mountpoint as $1 and the mountpoint vars in the environment.
function prepare_docker_args_for_mountpoint() {
local MOUNT_DIR="$1"
# shellcheck disable=SC2154 # $docker_kind: the kind of volume to mount on this OS; see mountpoints.sh
#display_alert "Handling Docker mountpoint" "${MOUNT_DIR} id: ${volume_id} - docker_kind: ${docker_kind}" "debug"
case "${docker_kind}" in
anonymous)
display_alert "Mounting" "anonymous volume for '${MOUNT_DIR}'" "debug"
# type=volume, without source=, is an anonymous volume -- will be auto cleaned up together with the container;
# this could also be a type=tmpfs if you had enough ram - but armbian already does tmpfs for you if you
# have enough RAM (inside the container) so don't bother.
DOCKER_ARGS+=("--mount" "type=volume,destination=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}")
;;
bind)
display_alert "Mounting" "bind mount for '${MOUNT_DIR}'" "debug"
mkdir -p "${SRC}/${MOUNT_DIR}"
DOCKER_ARGS+=("--mount" "type=bind,source=${SRC}/${MOUNT_DIR},target=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}")
;;
namedvolume)
display_alert "Mounting" "named volume id '${volume_id}' for '${MOUNT_DIR}'" "debug"
DOCKER_ARGS+=("--mount" "type=volume,source=armbian-${volume_id},destination=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}")
;;
*)
display_alert "Unknown Mountpoint Type" "unknown volume type '${docker_kind}' for '${MOUNT_DIR}'" "err"
exit 1
;;
esac
}
loop_over_armbian_mountpoints prepare_docker_args_for_mountpoint
# @TODO: auto-compute this list; just get the dirs and filter some out?
for MOUNT_DIR in "lib" "config" "extensions" "packages" "patch" "tools" "userpatches"; do
mkdir -p "${SRC}/${MOUNT_DIR}"
DOCKER_ARGS+=("--mount" "type=bind,source=${SRC}/${MOUNT_DIR},target=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}")
done
# eg: NOT on Darwin with Docker Desktop, that works simply with --priviledged and the extra caps.
# those actually _break_ Darwin with Docker Desktop, so we need to detect that.
# 20/Oct/2022: trying with Rancher Desktop in dockerd mode on Mac M1, this is required; also loops have to be hardcoded.
# How to detect this? It's Darwin, but not "real" Docker. How to find out?
if [[ "${DOCKER_SERVER_REQUIRES_LOOP_HACKS}" == "yes" ]]; then
display_alert "Adding /dev/loop* hacks for" "${DOCKER_ARMBIAN_HOST_OS_UNAME}" "debug"
DOCKER_ARGS+=("--security-opt=apparmor:unconfined") # mounting things inside the container on Ubuntu won't work without this https://github.com/moby/moby/issues/16429#issuecomment-217126586
@@ -345,6 +341,7 @@ function docker_cli_launch() {
display_alert "Relaunching in Docker" "${*}" "debug"
display_alert "Relaunching in Docker" "here comes the 🐳" "info"
local -i docker_build_result=1
if docker run -it "${DOCKER_ARGS[@]}" "${DOCKER_ARMBIAN_INITIAL_IMAGE_TAG}" /bin/bash "${DOCKER_ARMBIAN_TARGET_PATH}/compile.sh" "$@"; then
display_alert "Docker Build finished" "successfully" "info"

View File

@@ -0,0 +1,66 @@
function prepare_armbian_mountpoints_description_dict() {
# array for the generic armbian 'volumes' and their paths.
# bash dicts do NOT keep their insertion order, instead "hash order", which is a bit better than random for our purposes.
# keep an array with the correct order, unfortunately
declare -g -a ARMBIAN_MOUNTPOINTS_ARRAY=(
".tmp"
"output" "output/images" "output/debs" "output/logs"
"cache" "cache/gitballs" "cache/toolchain"
"cache/aptcache"
"cache/rootfs" "cache/initrd"
"cache/sources" "cache/sources/linux-kernel"
)
declare -A -g ARMBIAN_MOUNTPOINTS_DESC_DICT=(
[".tmp"]="docker_kind_linux=anonymous docker_kind_darwin=anonymous" # tmpfs, discard, anonymous; whatever you wanna call it. It just needs to be 100% local to the container, and there's very little value in being able to look at it from the host.
["output"]="docker_kind_linux=bind docker_kind_darwin=bind" # catch-all output. specific subdirs are mounted below. it's a bind mount by default on both Linux and Darwin.
["output/images"]="docker_kind_linux=bind docker_kind_darwin=bind" # 99% of users want this as the result of their build, no matter if it's slow or not. bind on both.
["output/debs"]="docker_kind_linux=bind docker_kind_darwin=namedvolume" # generated output .deb files. not everyone is interested in this: most users just want images. Linux has fast binds, so bound by default. Darwin has slow binds, so it's a volume by default.
["output/logs"]="docker_kind_linux=bind docker_kind_darwin=bind" # log files produced. 100% of users want this. Bind on both Linux and Darwin. Is used to integrate launcher and actual-build logs, so must exist and work otherwise confusion ensues.
["cache"]="docker_kind_linux=bind docker_kind_darwin=namedvolume" # catch-all cache, could be bind-mounted or a volume. On Darwin it's too slow to bind-mount, so it's a volume by default. On Linux, it's a bind-mount by default.
["cache/gitballs"]="docker_kind_linux=bind docker_kind_darwin=namedvolume" # tarballs of git repos, can be bind-mounted or a volume. On Darwin it's too slow to bind-mount, so it's a volume by default. On Linux, it's a bind-mount by default.
["cache/toolchain"]="docker_kind_linux=bind docker_kind_darwin=namedvolume" # toolchain cache, can be bind-mounted or a volume. On Darwin it's too slow to bind-mount, so it's a volume by default. On Linux, it's a bind-mount by default.
["cache/aptcache"]="docker_kind_linux=bind docker_kind_darwin=namedvolume" # .deb apt cache, replaces apt-cacher-ng. Can be bind-mounted or a volume. On Darwin it's too slow to bind-mount, so it's a volume by default. On Linux, it's a bind-mount by default.
["cache/rootfs"]="docker_kind_linux=bind docker_kind_darwin=namedvolume" # rootfs .tar.zst cache, can be bind-mounted or a volume. On Darwin it's too slow to bind-mount, so it's a volume by default. On Linux, it's a bind-mount by default.
["cache/initrd"]="docker_kind_linux=bind docker_kind_darwin=namedvolume" # initrd.img cache, can be bind-mounted or a volume. On Darwin it's too slow to bind-mount, so it's a volume by default. On Linux, it's a bind-mount by default.
["cache/sources"]="docker_kind_linux=bind docker_kind_darwin=namedvolume" # operating directory. many things are cloned in here, and some are even built inside. needs to be local to the container, so it's a volume by default. On Linux, it's a bind-mount by default.
["cache/sources/linux-kernel"]="docker_kind_linux=bind docker_kind_darwin=namedvolume" # working tree for kernel builds. huge. contains both sources and the built object files. needs to be local to the container, so it's a volume by default. On Linux, it's a bind-mount by default.
)
}
function loop_over_armbian_mountpoints() {
prepare_armbian_mountpoints_description_dict
# loop over all mountpoints and call the function passed as first argument
: "${1:?loop_over_mountpoints needs a function as first argument}"
local func="$1"
shift
local mountpoint
for mountpoint in "${ARMBIAN_MOUNTPOINTS_ARRAY[@]}"; do
# call func passing the key and the values as arguments
local values="${ARMBIAN_MOUNTPOINTS_DESC_DICT[$mountpoint]}"
eval "$values"
# This is contrived. Would be easier to just eval (and use Linux/Darwin instead of linux/darwin)
declare docker_kind="unknown"
case "${DOCKER_ARMBIAN_HOST_OS_UNAME}" in
Linux)
# shellcheck disable=SC2154 # defined during loop_over_armbian_mountpoints
docker_kind="${docker_kind_linux}"
;;
Darwin)
# shellcheck disable=SC2154 # defined during loop_over_armbian_mountpoints
docker_kind="${docker_kind_darwin}"
;;
*)
display_alert "Unsupported host OS" "${DOCKER_ARMBIAN_HOST_OS_UNAME} - cant map mountpoint to Docker volume" "warn"
;;
esac
# volume_id is the mountpoint with all slashes replaced with dashes
local volume_id="${mountpoint//\//-}"
# shellcheck disable=SC2086
eval "$values docker_kind=$docker_kind $func '$mountpoint'"
done
}

View File

@@ -361,6 +361,15 @@ set -o errexit ## set -e : exit the script if any statement returns a non-true
# shellcheck source=lib/functions/host/host-utils.sh
source "${SRC}"/lib/functions/host/host-utils.sh
# no errors tolerated. invoked before each sourced file to make sure.
#set -o pipefail # trace ERR through pipes - will be enabled "soon"
#set -o nounset ## set -u : exit the script if you try to use an uninitialised variable - one day will be enabled
set -o errtrace # trace ERR through - enabled
set -o errexit ## set -e : exit the script if any statement returns a non-true return value - enabled
### lib/functions/host/mountpoints.sh
# shellcheck source=lib/functions/host/mountpoints.sh
source "${SRC}"/lib/functions/host/mountpoints.sh
# no errors tolerated. invoked before each sourced file to make sure.
#set -o pipefail # trace ERR through pipes - will be enabled "soon"
#set -o nounset ## set -u : exit the script if you try to use an uninitialised variable - one day will be enabled