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

Allow moby-compose and docker-compose-plugin versions to be specified in devcontainer.json for Docker in Docker #841

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
17 changes: 11 additions & 6 deletions src/docker-in-docker/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "docker-in-docker",
"version": "2.9.0",
"version": "2.10.0",
"name": "Docker (Docker-in-Docker)",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/docker-in-docker",
"description": "Create child containers *inside* a container, independent from the host's docker instance. Installs Docker extension in the container along with needed CLIs.",
Expand All @@ -20,11 +20,6 @@
"default": true,
"description": "Install OSS Moby build instead of Docker CE"
},
"mobyBuildxVersion": {
"type": "string",
"default": "0.12.0",
"description": "Install a specific version of moby-buildx when using Moby. (2024-02-09: Microsoft's Package Manifest has mismatching filesize and SHA for 0.12.1; default is last known good version)"
},
"dockerDashComposeVersion": {
"type": "string",
"enum": [
Expand All @@ -50,6 +45,16 @@
"type": "boolean",
"default": true,
"description": "Install Docker Buildx"
},
"mobyBuildxVersion": {
Copy link
Member

Choose a reason for hiding this comment

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

Can we add test scenarios to validate/massage these two options? thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, will do

"type": "string",
"default": "0.12.0",
eljog marked this conversation as resolved.
Show resolved Hide resolved
"description": "Install a specific version of the moby-buildx package when using Moby. (2024-02-09: Microsoft's Package manifest has mismatching filesize and SHA for 0.12.1; the default is set to 0.12.0, the most recent working version)"
},
"composePackageVersion": {
"type": "string",
"default": "latest",
"description": "Install a specific version of the compose package (moby-compose for Moby, docker-compose-plugin for Docker). Note that this is the package version installed on the OS and not the Docker Compose version specified in 'dockerDashComposeVersion'"
}
},
"entrypoint": "/usr/local/share/docker-init.sh",
Expand Down
103 changes: 50 additions & 53 deletions src/docker-in-docker/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@

DOCKER_VERSION="${VERSION:-"latest"}" # The Docker/Moby Engine + CLI should match in version
USE_MOBY="${MOBY:-"true"}"
MOBY_BUILDX_VERSION="${MOBYBUILDXVERSION}"
DOCKER_DASH_COMPOSE_VERSION="${DOCKERDASHCOMPOSEVERSION:-"v1"}" # v1 or v2 or none
AZURE_DNS_AUTO_DETECTION="${AZUREDNSAUTODETECTION:-"true"}"
DOCKER_DEFAULT_ADDRESS_POOL="${DOCKERDEFAULTADDRESSPOOL}"
USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}"
INSTALL_DOCKER_BUILDX="${INSTALLDOCKERBUILDX:-"true"}"
MOBY_BUILDX_VERSION="${MOBYBUILDXVERSION}"
COMPOSE_PACKAGE_VERSION="${COMPOSEPACKAGEVERSION}"
MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc"
DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES="bookworm buster bullseye bionic focal jammy"
DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES="bookworm buster bullseye bionic focal hirsute impish jammy"
Expand Down Expand Up @@ -154,10 +155,10 @@ fi

# Set up the necessary apt repos (either Microsoft's or Docker's)
if [ "${USE_MOBY}" = "true" ]; then

# Name of open source engine/cli
engine_package_name="moby-engine"
cli_package_name="moby-cli"
compose_package_name="moby-compose"

# Import key safely and import Microsoft apt repo
curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg
Expand All @@ -166,6 +167,7 @@ else
# Name of licensed engine/cli
engine_package_name="docker-ce"
cli_package_name="docker-ce-cli"
compose_package_name="docker-compose-plugin"

# Import key safely and import Docker apt repo
curl -fsSL https://download.docker.com/linux/${ID}/gpg | gpg --dearmor > /usr/share/keyrings/docker-archive-keyring.gpg
Expand All @@ -175,72 +177,67 @@ fi
# Refresh apt lists
apt-get update

# Soft version matching
if [ "${DOCKER_VERSION}" = "latest" ] || [ "${DOCKER_VERSION}" = "lts" ] || [ "${DOCKER_VERSION}" = "stable" ]; then
# Empty, meaning grab whatever "latest" is in apt repo
engine_version_suffix=""
cli_version_suffix=""
else
# Fetch a valid version from the apt-cache (eg: the Microsoft repo appends +azure, breakfix, etc...)
docker_version_dot_escaped="${DOCKER_VERSION//./\\.}"
docker_version_dot_plus_escaped="${docker_version_dot_escaped//+/\\+}"
# Regex needs to handle debian package version number format: https://www.systutorials.com/docs/linux/man/5-deb-version/
docker_version_regex="^(.+:)?${docker_version_dot_plus_escaped}([\\.\\+ ~:-]|$)"
set +e # Don't exit if finding version fails - will handle gracefully
cli_version_suffix="=$(apt-cache madison ${cli_package_name} | awk -F"|" '{print $2}' | sed -e 's/^[ \t]*//' | grep -E -m 1 "${docker_version_regex}")"
engine_version_suffix="=$(apt-cache madison ${engine_package_name} | awk -F"|" '{print $2}' | sed -e 's/^[ \t]*//' | grep -E -m 1 "${docker_version_regex}")"
set -e
if [ -z "${engine_version_suffix}" ] || [ "${engine_version_suffix}" = "=" ] || [ -z "${cli_version_suffix}" ] || [ "${cli_version_suffix}" = "=" ] ; then
err "No full or partial Docker / Moby version match found for \"${DOCKER_VERSION}\" on OS ${ID} ${VERSION_CODENAME} (${architecture}). Available versions:"
apt-cache madison ${cli_package_name} | awk -F"|" '{print $2}' | grep -oP '^(.+:)?\K.+'
exit 1
fi
echo "engine_version_suffix ${engine_version_suffix}"
echo "cli_version_suffix ${cli_version_suffix}"
fi
# Helper to find appropriate package versions
get_package_version_suffix() {
local package_name=$1
local package_version=$2

# Version matching for moby-buildx
if [ "${USE_MOBY}" = "true" ]; then
if [ "${MOBY_BUILDX_VERSION}" = "latest" ]; then
if [ "${package_version}" = "latest" ] || [ "${package_version}" = "lts" ] || [ "${package_version}" = "stable" ]; then
# Empty, meaning grab whatever "latest" is in apt repo
buildx_version_suffix=""
echo ""
else
buildx_version_dot_escaped="${MOBY_BUILDX_VERSION//./\\.}"
buildx_version_dot_plus_escaped="${buildx_version_dot_escaped//+/\\+}"
buildx_version_regex="^(.+:)?${buildx_version_dot_plus_escaped}([\\.\\+ ~:-]|$)"
set +e
buildx_version_suffix="=$(apt-cache madison moby-buildx | awk -F"|" '{print $2}' | sed -e 's/^[ \t]*//' | grep -E -m 1 "${buildx_version_regex}")"
# Fetch a valid version from the apt-cache (eg: the Microsoft repo appends +azure, breakfix, etc...)
# Regex needs to handle debian package version number format: https://www.systutorials.com/docs/linux/man/5-deb-version/
local package_version_dot_escaped="${package_version//./\\.}"
local package_version_dot_plus_escaped="${package_version_dot_escaped//+/\\+}"
local package_version_regex="^(.+:)?${package_version_dot_plus_escaped}([\\.\\+ ~:-]|$)"

set +e # Catch errors so we can show more useful output
local package_version_suffix="=$(apt-cache madison ${package_name} | awk -F"|" '{print $2}' | sed -e 's/^[ \t]*//' | grep -E -m 1 "${package_version_regex}")"
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Can we indent all the code in between set commands for better code readability?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure thing. I typically don't consider set +e a scope, so I find the style a little confusing personally. Is there a bash styleguide this repo follows?

Copy link
Member

Choose a reason for hiding this comment

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

Is there a bash styleguide this repo follows?

I don't think there's a specific guide that we follow for this repo, trying to follow the historical stylings of these files (as well as happy to learn some new and standard styles) 😬

set -e
if [ -z "${buildx_version_suffix}" ] || [ "${buildx_version_suffix}" = "=" ]; then
err "No full or partial moby-buildx version match found for \"${MOBY_BUILDX_VERSION}\" on OS ${ID} ${VERSION_CODENAME} (${architecture}). Available versions:"
apt-cache madison moby-buildx | awk -F"|" '{print $2}' | grep -oP '^(.+:)?\K.+'

if [ -z "${package_version_suffix}" ] || [ "${package_version_suffix}" = "=" ]; then
err "No full or partial ${package_name} version match found for \"${package_version}\" on OS ${ID} ${VERSION_CODENAME} (${architecture}). Available versions:"
apt-cache madison ${package_name} | awk -F"|" '{print $2}' | grep -oP '^(.+:)?\K.+'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will this still echo to the log from within this function, or does it need to be redirected to stderr?

exit 1
fi
echo "buildx_version_suffix ${buildx_version_suffix}"

err "${package_name} package version suffix: ${package_version_suffix}"
echo $package_version_suffix
fi
fi
}

# Install Docker / Moby CLI if not already installed
if type docker > /dev/null 2>&1 && type dockerd > /dev/null 2>&1; then
echo "Docker / Moby CLI and Engine already installed."
else
# Determine required packages and versions. Exit if they cannot be found.
engine_version_suffix=$(get_package_version_suffix "${engine_package_name}" "${DOCKER_VERSION}") || exit 1
cli_version_suffix=$(get_package_version_suffix "${cli_package_name}" "${DOCKER_VERSION}") || exit 1
required_packages="${cli_package_name}${cli_version_suffix} ${engine_package_name}${engine_version_suffix}"

# Moby always uses buildx
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this true?

Copy link
Member

Choose a reason for hiding this comment

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

I don't think so!

Reasoning :buildx is a Docker CLI plugin that extends the docker build command with the full support of the features provided by Moby BuildKit builder toolkit. However, buildx is not the default build engine for Docker or Moby. The default builder in Docker is still the legacy docker build command. To use buildx, you need to either install it as a Docker CLI plugin or use a version of Docker that has buildx bundled with it (Docker 19.03 and later versions).

So, while Moby and Docker can use buildx for building images, it's not always used and depends on the specific configuration and version of Docker you're using.

(Feel free to correct if I have misunderstood)

if [ "${USE_MOBY}" = "true" ]; then
# Install engine
set +e # Handle error gracefully
apt-get -y install --no-install-recommends moby-cli${cli_version_suffix} moby-buildx${buildx_version_suffix} moby-engine${engine_version_suffix}
if [ $? -ne 0 ]; then
err "Packages for moby not available in OS ${ID} ${VERSION_CODENAME} (${architecture}). To resolve, either: (1) set feature option '\"moby\": false' , or (2) choose a compatible OS version (eg: 'ubuntu-20.04')."
exit 1
fi
set -e
moby_buildx_version_suffix=$(get_package_version_suffix "moby-buildx" "${MOBY_BUILDX_VERSION}") || exit 1
required_packages="${required_packages} moby-buildx${moby_buildx_version_suffix}"
fi

# Install compose
apt-get -y install --no-install-recommends moby-compose || err "Package moby-compose (Docker Compose v2) not available for OS ${ID} ${VERSION_CODENAME} (${architecture}). Skipping."
else
apt-get -y install --no-install-recommends docker-ce-cli${cli_version_suffix} docker-ce${engine_version_suffix}
# Install compose
apt-get -y install --no-install-recommends docker-compose-plugin || echo "(*) Package docker-compose-plugin (Docker Compose v2) not available for OS ${ID} ${VERSION_CODENAME} (${architecture}). Skipping."
# Install required packages (engine, CLI, and moby-buildx if moby)
set +e
apt-get -y install --no-install-recommends ${required_packages}
Copy link
Member

Choose a reason for hiding this comment

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

Same indent comment here..

if [ $? -ne 0 ]; then
if [ "${USE_MOBY}" = "true" ]; then
err "Packages for moby not available in OS ${ID} ${VERSION_CODENAME} (${architecture}). To resolve, either: (1) set feature option '\"moby\": false' , or (2) choose a compatible OS version (eg: 'ubuntu-20.04')."
else
err "Packages for Docker not available in OS ${ID} ${VERSION_CODENAME} (${architecture}). To resolve, choose compatible OS and package versions in your devcontainer.json file."
fi
exit 1
fi
set -e

# Install optional package, compose
compose_version_suffix=$(get_package_version_suffix "${compose_package_name}" "${COMPOSE_PACKAGE_VERSION}")
apt-get -y install --no-install-recommends ${compose_package_name}${compose_version_suffix} || err "Package ${compose_package_name}${compose_version_suffix} (Docker Compose v2) not available for OS ${ID} ${VERSION_CODENAME} (${architecture}). Skipping."
fi

echo "Finished installing docker / moby!"
Expand Down
Loading