Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[docker-outside-of-docker] - Fix externally-managed-environment python error for "bookworm" - solution to issue #1120 #1121

Merged
7 changes: 6 additions & 1 deletion src/docker-outside-of-docker/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "docker-outside-of-docker",
"version": "1.5.0",
"version": "1.6.0",
"name": "Docker (docker-outside-of-docker)",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/docker-outside-of-docker",
"description": "Re-use the host docker socket, adding the Docker CLI to a container. Feature invokes a script to enable using a forwarded Docker socket within a container to run Docker commands.",
Expand Down Expand Up @@ -39,6 +39,11 @@
"type": "boolean",
"default": true,
"description": "Install Docker Buildx"
},
"installDockerComposeSwitch": {
"type": "boolean",
"default": true,
"description": "Install Compose Switch (provided docker compose is available) which is a replacement to the Compose V1 docker-compose (python) executable. It translates the command line into Compose V2 docker compose then runs the latter."
}
},
"entrypoint": "/usr/local/share/docker-init.sh",
Expand Down
111 changes: 84 additions & 27 deletions src/docker-outside-of-docker/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ SOURCE_SOCKET="${SOURCE_SOCKET:-"/var/run/docker-host.sock"}"
TARGET_SOCKET="${TARGET_SOCKET:-"/var/run/docker.sock"}"
USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}"
INSTALL_DOCKER_BUILDX="${INSTALLDOCKERBUILDX:-"true"}"
INSTALL_DOCKER_COMPOSE_SWITCH="${INSTALLDOCKERCOMPOSESWITCH:-"true"}"
gauravsaini04 marked this conversation as resolved.
Show resolved Hide resolved
gauravsaini04 marked this conversation as resolved.
Show resolved Hide resolved

MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc"
DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES="bookworm buster bullseye bionic focal jammy noble"
Expand All @@ -27,6 +28,11 @@ set -e
# Clean up
rm -rf /var/lib/apt/lists/*

# Setup STDERR.
err() {
echo "(!) $*" >&2
}

if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1
Expand Down Expand Up @@ -177,7 +183,7 @@ install_compose_switch_fallback() {
echo -e "\n(!) Failed to fetch the latest artifacts for compose-switch v${compose_switch_version}..."
get_previous_version "${compose_switch_url}" "${repo_url}" compose_switch_version
echo -e "\nAttempting to install v${compose_switch_version}"
curl -fsSL "https://github.com/docker/compose-switch/releases/download/v${compose_switch_version}/docker-compose-linux-${architecture}" -o /usr/local/bin/docker-compose
curl -fsSL "https://github.com/docker/compose-switch/releases/download/v${compose_switch_version}/docker-compose-linux-${architecture}" -o /usr/local/bin/compose-switch
}

# Ensure apt is in non-interactive to avoid prompts
Expand Down Expand Up @@ -273,6 +279,19 @@ if [ "${USE_MOBY}" = "true" ]; then
fi
fi


docker_home="/usr/libexec/docker"
cli_plugins_dir="${docker_home}/cli-plugins"

install_compose_fallback(){
local url=$1
local repo_url=$(get_github_api_repo_url "$url")
echo -e "\n(!) Failed to fetch the latest artifacts for docker-compose v${compose_version}..."
get_previous_version "${url}" "${repo_url}" compose_version
echo -e "\nAttempting to install v${compose_version}"
curl -fsSL "https://github.com/docker/compose/releases/download/v${compose_version}/docker-compose-linux-${target_compose_arch}" -o ${docker_compose_path}
}

# Install Docker / Moby CLI if not already installed
if type docker > /dev/null 2>&1; then
echo "Docker / Moby CLI already installed."
Expand Down Expand Up @@ -302,44 +321,82 @@ fi

# If 'docker-compose' command is to be included
if [ "${DOCKER_DASH_COMPOSE_VERSION}" != "none" ]; then
case "${architecture}" in
amd64) target_compose_arch=x86_64 ;;
arm64) target_compose_arch=aarch64 ;;
*)
echo "(!) Docker outside of docker does not support machine architecture '$architecture'. Please use an x86-64 or ARM64 machine."
exit 1
esac
docker_compose_path="/usr/local/bin/docker-compose"
# Install Docker Compose if not already installed and is on a supported architecture
if type docker-compose > /dev/null 2>&1; then
echo "Docker Compose already installed."
elif [ "${DOCKER_DASH_COMPOSE_VERSION}" = "v1" ]; then
TARGET_COMPOSE_ARCH="$(uname -m)"
if [ "${TARGET_COMPOSE_ARCH}" = "amd64" ]; then
TARGET_COMPOSE_ARCH="x86_64"
fi
if [ "${TARGET_COMPOSE_ARCH}" != "x86_64" ]; then
err "The final Compose V1 release, version 1.29.2, was May 10, 2021. These packages haven't received any security updates since then. Use at your own risk."
INSTALL_DOCKER_COMPOSE_SWITCH="false"

if [ "${target_compose_arch}" = "x86_64" ]; then
echo "(*) Installing docker compose v1..."
curl -fsSL "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-Linux-x86_64" -o ${docker_compose_path}
chmod +x ${docker_compose_path}

# Download the SHA256 checksum
DOCKER_COMPOSE_SHA256="$(curl -sSL "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-Linux-x86_64.sha256" | awk '{print $1}')"
echo "${DOCKER_COMPOSE_SHA256} ${docker_compose_path}" > docker-compose.sha256sum
sha256sum -c docker-compose.sha256sum --ignore-missing
elif [ "${VERSION_CODENAME}" = "bookworm" ]; then
err "Docker compose v1 is unavailable for 'bookworm' on Arm64. Kindly switch to use v2"
exit 1
else
# Use pip to get a version that runs on this architecture
check_packages python3-minimal python3-pip libffi-dev python3-venv
export PIPX_HOME=/usr/local/pipx
mkdir -p ${PIPX_HOME}
export PIPX_BIN_DIR=/usr/local/bin
export PYTHONUSERBASE=/tmp/pip-tmp
export PIP_CACHE_DIR=/tmp/pip-tmp/cache
pipx_bin=pipx
if ! type pipx > /dev/null 2>&1; then
pip3 install --disable-pip-version-check --no-cache-dir --user pipx
pipx_bin=/tmp/pip-tmp/bin/pipx
fi
${pipx_bin} install --pip-args '--no-cache-dir --force-reinstall' docker-compose
rm -rf /tmp/pip-tmp
else
compose_v1_version="1"
find_version_from_git_tags compose_v1_version "https://github.com/docker/compose" "tags/"
echo "(*) Installing docker-compose ${compose_v1_version}..."
curl -fsSL "https://github.com/docker/compose/releases/download/${compose_v1_version}/docker-compose-Linux-x86_64" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
echo "(*) Installing docker compose v1 via pip..."
export PYTHONUSERBASE=/usr/local
pip3 install --disable-pip-version-check --no-cache-dir --user "Cython<3.0" pyyaml wheel docker-compose --no-build-isolation
fi
else
echo "(*) Installing compose-switch as docker-compose..."
compose_version=${DOCKER_DASH_COMPOSE_VERSION#v}
docker_compose_url="https://github.com/docker/compose"
find_version_from_git_tags compose_version "$docker_compose_url" "tags/v"
echo "(*) Installing docker-compose ${compose_version}..."
curl -fsSL "https://github.com/docker/compose/releases/download/v${compose_version}/docker-compose-linux-${target_compose_arch}" -o ${docker_compose_path} || {
if [[ $DOCKER_DASH_COMPOSE_VERSION == "latest" ]]; then
install_compose_fallback "$docker_compose_url" "$compose_version" "$target_compose_arch" "$docker_compose_path"
else
echo -e "Error: Failed to install docker-compose v${compose_version}"
fi
}
chmod +x ${docker_compose_path}

# Download the SHA256 checksum
DOCKER_COMPOSE_SHA256="$(curl -sSL "https://github.com/docker/compose/releases/download/v${compose_version}/docker-compose-linux-${target_compose_arch}.sha256" | awk '{print $1}')"
echo "${DOCKER_COMPOSE_SHA256} ${docker_compose_path}" > docker-compose.sha256sum
sha256sum -c docker-compose.sha256sum --ignore-missing

mkdir -p ${cli_plugins_dir}
cp ${docker_compose_path} ${cli_plugins_dir}
fi
fi

# Install docker-compose switch if not already installed - https://github.com/docker/compose-switch#manual-installation
if [ "${INSTALL_DOCKER_COMPOSE_SWITCH}" = "true" ] && ! type compose-switch > /dev/null 2>&1; then
if type docker-compose > /dev/null 2>&1; then
echo "(*) Installing compose-switch..."
current_compose_path="$(which docker-compose)"
target_compose_path="$(dirname "${current_compose_path}")/docker-compose-v1"
compose_switch_version="latest"
compose_switch_url="https://github.com/docker/compose-switch"
find_version_from_git_tags compose_switch_version "${compose_switch_url}"
curl -fsSL "https://github.com/docker/compose-switch/releases/download/v${compose_switch_version}/docker-compose-linux-${architecture}" -o /usr/local/bin/docker-compose || install_compose_switch_fallback "${compose_switch_url}"
chmod +x /usr/local/bin/docker-compose
curl -fsSL "https://github.com/docker/compose-switch/releases/download/v${compose_switch_version}/docker-compose-linux-${architecture}" -o /usr/local/bin/compose-switch || install_compose_switch_fallback "${compose_switch_url}"
chmod +x /usr/local/bin/compose-switch
# TODO: Verify checksum once available: https://github.com/docker/compose-switch/issues/11
# Setup v1 CLI as alternative in addition to compose-switch (which maps to v2)
mv "${current_compose_path}" "${target_compose_path}"
update-alternatives --install ${docker_compose_path} docker-compose /usr/local/bin/compose-switch 99
update-alternatives --install ${docker_compose_path} docker-compose "${target_compose_path}" 1
else
err "Skipping installation of compose-switch as docker compose is unavailable..."
fi
fi

Expand Down
10 changes: 2 additions & 8 deletions test/docker-outside-of-docker/docker_build_compose_fallback.sh
Original file line number Diff line number Diff line change
Expand Up @@ -112,20 +112,14 @@ get_previous_version() {
local variable_name=$3
local mode=$4
prev_version=${!variable_name}

output=$(curl -s "$repo_url");

check_packages jq

message=$(echo "$output" | jq -r '.message')

if [[ $mode == 'mode1' ]]; then
message="API rate limit exceeded"
else
message=""
fi

if [[ $message == "API rate limit exceeded"* ]]; then
if [[ $message == "API rate limit exceeded"* ]] || [[ $mode == 'mode1' ]]; then
echo -e "\nAn attempt to find latest version using GitHub Api Failed... \nReason: ${message}"
echo -e "\nAttempting to find latest version using GitHub tags."
find_prev_version_from_git_tags prev_version "$url" "tags/v"
Expand Down
29 changes: 29 additions & 0 deletions test/docker-outside-of-docker/docker_install_compose_switch.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash

set -e

# Optional: Import test library
source dev-container-features-test-lib

# Check if compose-switch is installed
check_compose_switch_installation() {
COMPOSE_SWITCH_BINARY="/usr/local/bin/compose-switch"
# Check if the binary exists
if [ ! -x "$COMPOSE_SWITCH_BINARY" ]; then
echo "compose-switch binary not found at $COMPOSE_SWITCH_BINARY"
exit 1
else
compose_switch_version=$("$COMPOSE_SWITCH_BINARY" --version | awk '{print $4}')
if [ -z "$compose_switch_version" ]; then
echo "Unable to determine compose-switch version"
else
echo "compose-switch version: $compose_switch_version"
echo -e "\n✅ compose-switch is installed"
fi
fi
}

check "Check whether compose-switch is installed" check_compose_switch_installation

reportResults

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash

set -e

# Optional: Import test library
source dev-container-features-test-lib

# Check if compose-switch is installed
check_compose_switch_installation() {
COMPOSE_SWITCH_BINARY="/usr/local/bin/compose-switch"
# Check if the binary exists
if [ ! -x "$COMPOSE_SWITCH_BINARY" ]; then
echo "compose-switch binary not found at $COMPOSE_SWITCH_BINARY"
echo -e "\n❎ compose-switch is not installed"
else
compose_switch_version=$("$COMPOSE_SWITCH_BINARY" --version | awk '{print $4}')
if [ -z "$compose_switch_version" ]; then
echo "Unable to determine compose-switch version"
else
echo "compose-switch version: $compose_switch_version"
fi
exit 1
fi
}

check "Check whether compose-switch is installed" check_compose_switch_installation

reportResults

22 changes: 22 additions & 0 deletions test/docker-outside-of-docker/docker_python_bookworm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash

set -e

# Optional: Import test library
source dev-container-features-test-lib

# Definition specific tests
check "docker-buildx" bash -c "docker buildx version"
check "docker-buildx-path" bash -c "ls -la /usr/libexec/docker/cli-plugins/docker-buildx"

check "docker-buildx" docker buildx version
check "docker-build" docker build ./

check "installs docker-compose v2 install" bash -c "type docker-compose"
check "docker compose" bash -c "docker compose version | grep -E '2.[0-9]+.[0-9]+'"
check "docker-compose" bash -c "docker-compose --version | grep -E '2.[0-9]+.[0-9]+'"

check "installs compose-switch as docker-compose" bash -c "[[ -f /usr/local/bin/docker-compose ]]"

# Report result
reportResults
30 changes: 30 additions & 0 deletions test/docker-outside-of-docker/scenarios.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,5 +142,35 @@
"mobyBuildxVersion": "0.12.0"
}
}
},
"docker_python_bookworm": {
"image": "mcr.microsoft.com/devcontainers/base:bookworm",
gauravsaini04 marked this conversation as resolved.
Show resolved Hide resolved
"features": {
"docker-outside-of-docker": {
"moby": true,
"installDockerBuildx": true,
"dockerDashComposeVersion": "v2"
}
}
},
"docker_not_install_compose_switch": {
"image": "mcr.microsoft.com/devcontainers/base:ubuntu-20.04",
"features": {
"docker-outside-of-docker": {
"dockerDashComposeVersion": "latest",
"installDockerComposeSwitch": false
}
},
"containerUser": "vscode"
},
"docker_install_compose_switch": {
"image": "mcr.microsoft.com/devcontainers/base:ubuntu-20.04",
"features": {
"docker-outside-of-docker": {
"dockerDashComposeVersion": "latest",
"installDockerComposeSwitch": true
}
},
"containerUser": "vscode"
}
}