From ec42eaafc7647ae74987ca340ea8c31ffcb5dff6 Mon Sep 17 00:00:00 2001 From: Ben Menesini Date: Fri, 9 Feb 2024 20:04:42 -0800 Subject: [PATCH 1/4] Allow moby-compose and docker-compose-plugin versions to be specified in devcontainer.json This changeset allows users to specify the versions of the docker-compose-plugin and moby-compose packages to be installed as part of the docker-in-docker devcontainer feature. This is provided as a way to allow users to pin the underlying versions of these packages in cases where newer versions may not be working or available Also included in this changeset is a refactor of the way package versions are discovered to ensure the process is consistent and DRY --- .../devcontainer-feature.json | 17 ++- src/docker-in-docker/install.sh | 108 +++++++++--------- 2 files changed, 66 insertions(+), 59 deletions(-) diff --git a/src/docker-in-docker/devcontainer-feature.json b/src/docker-in-docker/devcontainer-feature.json index 744bc1d81..6fc8abd59 100644 --- a/src/docker-in-docker/devcontainer-feature.json +++ b/src/docker-in-docker/devcontainer-feature.json @@ -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.", @@ -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": [ @@ -50,6 +45,16 @@ "type": "boolean", "default": true, "description": "Install Docker Buildx" + }, + "mobyBuildxVersion": { + "type": "string", + "default": "0.12.0", + "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", diff --git a/src/docker-in-docker/install.sh b/src/docker-in-docker/install.sh index db3cefc6a..383bdf1b1 100755 --- a/src/docker-in-docker/install.sh +++ b/src/docker-in-docker/install.sh @@ -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" @@ -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 @@ -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 @@ -175,72 +177,72 @@ 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 +# Call inside a subshell to prevent missing packages from exiting the main process +# Examples: +# optional_package_suffix=$(get_package_version_suffix "some-skippable-package" "0.0.1") +# required_package_suffix=get_package_version_suffix "some-required-package" "0.0.1" +# +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}")" 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.+' exit 1 fi - echo "buildx_version_suffix ${buildx_version_suffix}" + + echo "${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 + engine_version_suffix=get_package_version_suffix "${engine_package_name}" "${DOCKER_VERSION}" + cli_version_suffix=get_package_version_suffix "${cli_package_name}" "${DOCKER_VERSION}" + required_packages="${cli_package_name}${cli_version_suffix} ${engine_package_name}${engine_version_suffix}" + + # Moby always uses buildx 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}" + 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} + 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 compose + compose_version_suffix=$(get_package_verson_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!" From 48fcb4ee30b3ae4f99e79a7cabd857b6ba1c782d Mon Sep 17 00:00:00 2001 From: Ben Menesini Date: Fri, 9 Feb 2024 23:53:38 -0800 Subject: [PATCH 2/4] Fix syntax error --- src/docker-in-docker/install.sh | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/docker-in-docker/install.sh b/src/docker-in-docker/install.sh index 383bdf1b1..94fc6f68e 100755 --- a/src/docker-in-docker/install.sh +++ b/src/docker-in-docker/install.sh @@ -178,11 +178,6 @@ fi apt-get update # Helper to find appropriate package versions -# Call inside a subshell to prevent missing packages from exiting the main process -# Examples: -# optional_package_suffix=$(get_package_version_suffix "some-skippable-package" "0.0.1") -# required_package_suffix=get_package_version_suffix "some-required-package" "0.0.1" -# get_package_version_suffix() { local package_name=$1 local package_version=$2 @@ -207,7 +202,7 @@ get_package_version_suffix() { exit 1 fi - echo "${package_name} package version suffix: ${package_version_suffix}" + err "${package_name} package version suffix: ${package_version_suffix}" echo $package_version_suffix fi } @@ -216,10 +211,10 @@ get_package_version_suffix() { 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 - engine_version_suffix=get_package_version_suffix "${engine_package_name}" "${DOCKER_VERSION}" - cli_version_suffix=get_package_version_suffix "${cli_package_name}" "${DOCKER_VERSION}" - required_packages="${cli_package_name}${cli_version_suffix} ${engine_package_name}${engine_version_suffix}" + # 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}" || exit 1 # Moby always uses buildx if [ "${USE_MOBY}" = "true" ]; then @@ -240,7 +235,7 @@ else fi set -e - # Install compose + # Install optional package, compose compose_version_suffix=$(get_package_verson_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 From 4aafb7f0105004b9b0dada8657d2abffda307ab5 Mon Sep 17 00:00:00 2001 From: Ben Menesini Date: Fri, 9 Feb 2024 23:58:44 -0800 Subject: [PATCH 3/4] Fix misplaced code --- src/docker-in-docker/install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/docker-in-docker/install.sh b/src/docker-in-docker/install.sh index 94fc6f68e..03d5ce95f 100755 --- a/src/docker-in-docker/install.sh +++ b/src/docker-in-docker/install.sh @@ -214,11 +214,11 @@ 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}" || exit 1 + required_packages="${cli_package_name}${cli_version_suffix} ${engine_package_name}${engine_version_suffix}" # Moby always uses buildx if [ "${USE_MOBY}" = "true" ]; then - moby_buildx_version_suffix=get_package_version_suffix "moby-buildx" "${MOBY_BUILDX_VERSION}" + 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 From f82499eee7237dc97375c864df06cb84725857dd Mon Sep 17 00:00:00 2001 From: Ben Menesini Date: Tue, 13 Feb 2024 09:16:01 -0800 Subject: [PATCH 4/4] Update src/docker-in-docker/install.sh Co-authored-by: Samruddhi Khandale --- src/docker-in-docker/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docker-in-docker/install.sh b/src/docker-in-docker/install.sh index 03d5ce95f..bdad9d56b 100755 --- a/src/docker-in-docker/install.sh +++ b/src/docker-in-docker/install.sh @@ -236,7 +236,7 @@ else set -e # Install optional package, compose - compose_version_suffix=$(get_package_verson_suffix "${compose_package_name}" "${COMPOSE_PACKAGE_VERSION}") + 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