Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 33 additions & 20 deletions lib/functions/host/docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,35 @@ function get_docker_info_once() {
if [[ -z "${DOCKER_INFO}" ]]; then
declare -g DOCKER_INFO
declare -g DOCKER_IN_PATH="no"

# if "docker" is in the PATH...
declare -g DOCKER_COMMAND="docker"
declare -g DOCKER_NETWORK=""
declare -g DOCKER_MOUNT_OPTS=""

# Detect container engine - prefer Docker for compatibility, but support Podman
if [[ -n "$(command -v docker)" ]]; then
display_alert "Docker is in the path" "Docker in PATH" "debug"
DOCKER_COMMAND="docker"
display_alert "Docker detected" "Using Docker" "debug"
DOCKER_IN_PATH="yes"
elif [[ -n "$(command -v podman)" ]]; then
DOCKER_COMMAND="sudo podman"
DOCKER_NETWORK="--network host"
DOCKER_MOUNT_OPTS=",suid,dev" # Required mount options for Podman
display_alert "Podman detected" "Using Podman with sudo (required for Armbian builds)" "info"
fi

# Shenanigans to go around error control & capture output in the same effort.
DOCKER_INFO="$({ docker info 2> /dev/null && echo "DOCKER_INFO_OK"; } || true)"
DOCKER_INFO="$({ $DOCKER_COMMAND info 2> /dev/null && echo "DOCKER_INFO_OK"; } || true)"
declare -g -r DOCKER_INFO="${DOCKER_INFO}" # readonly

declare -g DOCKER_INFO_OK="no"
if [[ "${DOCKER_INFO}" =~ "DOCKER_INFO_OK" ]]; then
DOCKER_INFO_OK="yes"
fi
declare -g -r DOCKER_INFO_OK="${DOCKER_INFO_OK}" # readonly
declare -g -r DOCKER_COMMAND="${DOCKER_COMMAND}"
declare -g -r DOCKER_NETWORK="${DOCKER_NETWORK}"
declare -g -r DOCKER_MOUNT_OPTS="${DOCKER_MOUNT_OPTS}"
fi
return 0
}
Expand All @@ -73,8 +86,8 @@ function is_docker_ready_to_go() {
display_alert "Can't use Docker" "Actually ALREADY UNDER DOCKER!" "debug"
return 1
fi
if [[ -z "$(command -v docker)" ]]; then
display_alert "Can't use Docker" "docker command not found" "debug"
if [[ -z "$(command -v docker)" ]] && [[ -z "$(command -v podman)" ]]; then
display_alert "Can't use Docker" "docker or podman command not found" "debug"
return 1
fi

Expand All @@ -97,19 +110,19 @@ function cli_handle_docker() {
# Purge Armbian Docker images
if [[ "${1}" == dockerpurge && -f /etc/debian_version ]]; then
display_alert "Purging Armbian Docker containers" "" "wrn"
docker container ls -a | grep armbian | awk '{print $1}' | xargs docker container rm &> /dev/null
docker image ls | grep armbian | awk '{print $3}' | xargs docker image rm &> /dev/null
$DOCKER_COMMAND container ls -a | grep armbian | awk '{print $1}' | xargs $DOCKER_COMMAND container rm &> /dev/null
$DOCKER_COMMAND image ls | grep armbian | awk '{print $3}' | xargs $DOCKER_COMMAND image rm &> /dev/null
# removes "dockerpurge" from $1, thus $2 becomes $1
shift
set -- "docker" "$@"
set -- $DOCKER_COMMAND "$@"
fi

# Docker shell
if [[ "${1}" == docker-shell ]]; then
# this swaps the value of $1 with 'docker', and life continues
shift
SHELL_ONLY=yes
set -- "docker" "$@"
set -- $DOCKER_COMMAND "$@"
fi

}
Expand Down Expand Up @@ -323,7 +336,7 @@ function docker_cli_build_dockerfile() {

if [[ "${do_force_pull}" == "no" ]]; then
# Check if the base image is up to date.
local_image_sha="$(docker images --no-trunc --quiet "${DOCKER_ARMBIAN_BASE_IMAGE}")"
local_image_sha="$($DOCKER_COMMAND images --no-trunc --quiet "${DOCKER_ARMBIAN_BASE_IMAGE}")"
display_alert "Checking if base image exists at all" "local_image_sha: '${local_image_sha}'" "debug"
if [[ -n "${local_image_sha}" ]]; then
display_alert "Armbian docker image" "already exists: ${DOCKER_ARMBIAN_BASE_IMAGE}" "info"
Expand All @@ -336,10 +349,10 @@ function docker_cli_build_dockerfile() {
if [[ "${do_force_pull:-yes}" == "yes" ]]; then
display_alert "Pulling" "${DOCKER_ARMBIAN_BASE_IMAGE}" "info"
local pull_failed="yes"
run_host_command_logged docker pull "${DOCKER_ARMBIAN_BASE_IMAGE}" && pull_failed="no"
run_host_command_logged $DOCKER_COMMAND pull "${DOCKER_ARMBIAN_BASE_IMAGE}" && pull_failed="no"

if [[ "${pull_failed}" == "no" ]]; then
local_image_sha="$(docker images --no-trunc --quiet "${DOCKER_ARMBIAN_BASE_IMAGE}")"
local_image_sha="$($DOCKER_COMMAND images --no-trunc --quiet "${DOCKER_ARMBIAN_BASE_IMAGE}")"
display_alert "New local image sha after pull" "local_image_sha: ${local_image_sha}" "debug"
# print current date and time in epoch format; touches mtime of file
echo "${DOCKER_ARMBIAN_BASE_IMAGE}|${local_image_sha}|$(date +%s)" >> "${docker_marker_dir}"/last-pull
Expand All @@ -360,7 +373,7 @@ function docker_cli_build_dockerfile() {
display_alert "Building" "Dockerfile via '${DOCKER_BUILDX_OR_BUILD[*]}'" "info"

BUILDKIT_COLORS="run=123,20,245:error=yellow:cancel=blue:warning=white" \
run_host_command_logged docker "${DOCKER_BUILDX_OR_BUILD[@]}" -t "${DOCKER_ARMBIAN_INITIAL_IMAGE_TAG}" -f "${SRC}"/Dockerfile "${SRC}"
run_host_command_logged $DOCKER_COMMAND "${DOCKER_BUILDX_OR_BUILD[@]}" -t "${DOCKER_ARMBIAN_INITIAL_IMAGE_TAG}" -f "${SRC}"/Dockerfile "${SRC}"
}

function docker_cli_prepare_launch() {
Expand Down Expand Up @@ -503,16 +516,16 @@ function docker_cli_prepare_launch() {
# 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}")
DOCKER_ARGS+=("--mount" "type=volume,destination=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}${DOCKER_MOUNT_OPTS}")
;;
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}")
DOCKER_ARGS+=("--mount" "type=bind,source=${SRC}/${MOUNT_DIR},target=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}${DOCKER_MOUNT_OPTS}")
;;
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}")
DOCKER_ARGS+=("--mount" "type=volume,source=armbian-${volume_id},destination=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}${DOCKER_MOUNT_OPTS}")
;;
Comment on lines +519 to 529
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

❓ Verification inconclusive

Fix invalid --mount syntax; pass options via options=… instead of appending raw flags to destination.

Appending ",suid,dev" after destination=… is not a valid key=value pair for Docker/Podman --mount. Use options=… so both engines parse mount flags correctly.

Apply this diff:

-				DOCKER_ARGS+=("--mount" "type=volume,destination=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}${DOCKER_MOUNT_OPTS}")
+				DOCKER_ARGS+=("--mount" "type=volume,destination=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}${DOCKER_MOUNT_OPTS:+,options=${DOCKER_MOUNT_OPTS}}")
...
-				DOCKER_ARGS+=("--mount" "type=bind,source=${SRC}/${MOUNT_DIR},target=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}${DOCKER_MOUNT_OPTS}")
+				DOCKER_ARGS+=("--mount" "type=bind,source=${SRC}/${MOUNT_DIR},target=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}${DOCKER_MOUNT_OPTS:+,options=${DOCKER_MOUNT_OPTS}}")
...
-				DOCKER_ARGS+=("--mount" "type=volume,source=armbian-${volume_id},destination=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}${DOCKER_MOUNT_OPTS}")
+				DOCKER_ARGS+=("--mount" "type=volume,source=armbian-${volume_id},destination=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}${DOCKER_MOUNT_OPTS:+,options=${DOCKER_MOUNT_OPTS}}")

Fix invalid --mount syntax; pass options via options=… instead of appending raw flags to destination.

Appending ",suid,dev" (or similar) after destination=… is not a valid key=value pair for Docker/Podman --mount. Use options=… so both engines parse mount flags correctly.

Apply this diff:

-				DOCKER_ARGS+=("--mount" "type=volume,destination=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}${DOCKER_MOUNT_OPTS}")
+				DOCKER_ARGS+=("--mount" "type=volume,destination=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}${DOCKER_MOUNT_OPTS:+,options=${DOCKER_MOUNT_OPTS}}")
...
-				DOCKER_ARGS+=("--mount" "type=bind,source=${SRC}/${MOUNT_DIR},target=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}${DOCKER_MOUNT_OPTS}")
+				DOCKER_ARGS+=("--mount" "type=bind,source=${SRC}/${MOUNT_DIR},target=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}${DOCKER_MOUNT_OPTS:+,options=${DOCKER_MOUNT_OPTS}}")
...
-				DOCKER_ARGS+=("--mount" "type=volume,source=armbian-${volume_id},destination=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}${DOCKER_MOUNT_OPTS}")
+				DOCKER_ARGS+=("--mount" "type=volume,source=armbian-${volume_id},destination=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}${DOCKER_MOUNT_OPTS:+,options=${DOCKER_MOUNT_OPTS}}")

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In lib/functions/host/docker.sh around lines 519-529, the --mount invocations
append raw mount flags to the destination (destination=...${DOCKER_MOUNT_OPTS})
which is invalid; change them to use options=... instead. For each
DOCKER_ARGS+=("--mount"
"...destination=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}${DOCKER_MOUNT_OPTS}")
line, split DOCKER_MOUNT_OPTS into an options clause (strip any leading comma)
and append ,options=${OPTIONS} to the --mount string (omit the options= part
entirely when DOCKER_MOUNT_OPTS is empty), e.g.
destination=.../${MOUNT_DIR}[,options=${DOCKER_MOUNT_OPTS_STRIPPED}]; apply this
for volume, bind and namedvolume cases so Docker/Podman parse mount flags
correctly.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DOCKER_MOUNT_OPTS is set at podman detection to the required flags or kept empty for docker. There should be no potential for mixing the flags with docker.

*)
display_alert "Unknown Mountpoint Type" "unknown volume type '${docker_kind}' for '${MOUNT_DIR}'" "err"
Expand Down Expand Up @@ -585,13 +598,13 @@ function docker_cli_launch() {
# The amount of privileges and capabilities given is a bare minimum needed for losetup to work
if [[ ! -e /dev/loop0 ]]; then
display_alert "Running losetup in a temporary container" "because no loop devices exist" "info"
run_host_command_logged docker run --rm --privileged --cap-add=MKNOD "${DOCKER_ARMBIAN_INITIAL_IMAGE_TAG}" /usr/sbin/losetup -f
run_host_command_logged $DOCKER_COMMAND run $DOCKER_NETWORK --rm --privileged --cap-add=MKNOD "${DOCKER_ARMBIAN_INITIAL_IMAGE_TAG}" /usr/sbin/losetup -f
fi

display_alert "-----------------Relaunching in Docker after ${SECONDS}s------------------" "here comes the 🐳" "info"

local -i docker_build_result
if docker run "${DOCKER_ARGS[@]}" "${DOCKER_ARMBIAN_INITIAL_IMAGE_TAG}" /bin/bash "${DOCKER_ARMBIAN_TARGET_PATH}/compile.sh" "${ARMBIAN_CLI_FINAL_RELAUNCH_ARGS[@]}"; then
if $DOCKER_COMMAND run $DOCKER_NETWORK "${DOCKER_ARGS[@]}" "${DOCKER_ARMBIAN_INITIAL_IMAGE_TAG}" /bin/bash "${DOCKER_ARMBIAN_TARGET_PATH}/compile.sh" "${ARMBIAN_CLI_FINAL_RELAUNCH_ARGS[@]}"; then
docker_build_result=$? # capture exit code of test done in the line above.
display_alert "-------------Docker run finished after ${SECONDS}s------------------------" "🐳 successful" "info"
else
Expand Down Expand Up @@ -623,8 +636,8 @@ function docker_purge_deprecated_volumes() {
for mountpoint in "${ARMBIAN_MOUNTPOINTS_DEPRECATED[@]}"; do
local volume_id="armbian-${mountpoint//\//-}"
display_alert "Purging deprecated Docker volume" "${volume_id}" "info"
if docker volume inspect "${volume_id}" &> /dev/null; then
run_host_command_logged docker volume rm "${volume_id}"
if $DOCKER_COMMAND volume inspect "${volume_id}" &> /dev/null; then
run_host_command_logged $DOCKER_COMMAND volume rm "${volume_id}"
display_alert "Purged deprecated Docker volume" "${volume_id} OK" "info"
else
display_alert "Deprecated Docker volume not found" "${volume_id} OK" "info"
Expand Down