Skip to content

[anaconda] - feature installation support for arm64/ aarch64. #1342

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

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
3 changes: 2 additions & 1 deletion src/anaconda/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ conda install python=3.7
## OS Support

This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.

Also RHEL based linux distributions such as almalinux, rockylinux, fedora are supported now.
Please do note that Alpine and cbl-mariner aren't supported due system level restrictions with the anaconda installer in alpine linux and `groupadd`, `usermod`, `awk` commands not being supported in mariner.
`bash` is required to execute the `install.sh` script.


Expand Down
2 changes: 1 addition & 1 deletion src/anaconda/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "anaconda",
"version": "1.0.13",
"version": "1.0.14",
"name": "Anaconda",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/anaconda",
"options": {
Expand Down
211 changes: 192 additions & 19 deletions src/anaconda/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,63 @@ USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}"
UPDATE_RC="${UPDATE_RC:-"true"}"
CONDA_DIR="${CONDA_DIR:-"/usr/local/conda"}"

set -eux
set -exo pipefail
export DEBIAN_FRONTEND=noninteractive

# Detect package manager and set install command
detect_package_manager() {
if command -v apt-get > /dev/null; then
PKG_MANAGER="apt-get"
PKG_UPDATE="apt-get update -y"
PKG_INSTALL="apt-get -y install --no-install-recommends"
PKG_CLEAN="apt-get -y clean"
PKG_LISTS="/var/lib/apt/lists/*"
PKG_QUERY="dpkg -s"
elif command -v apk > /dev/null; then
PKG_MANAGER="apk"
PKG_UPDATE="apk update"
PKG_INSTALL="apk add --no-cache"
PKG_CLEAN="rm -rf /var/cache/apk/*"
PKG_LISTS="/var/cache/apk/*"
PKG_QUERY="apk info -e"
elif command -v dnf > /dev/null; then
PKG_MANAGER="dnf"
PKG_UPDATE="dnf -y makecache"
PKG_INSTALL="dnf -y install"
PKG_CLEAN="dnf clean all"
PKG_LISTS="/var/cache/dnf/*"
PKG_QUERY="rpm -q"
elif command -v microdnf > /dev/null; then
PKG_MANAGER="microdnf"
PKG_UPDATE="microdnf update"
PKG_INSTALL="microdnf install -y"
PKG_CLEAN="microdnf clean all"
PKG_LISTS="/var/cache/yum/*"
PKG_QUERY="rpm -q"
elif command -v tdnf > /dev/null; then
PKG_MANAGER="tdnf"
PKG_UPDATE="tdnf makecache"
PKG_INSTALL="tdnf install -y"
PKG_CLEAN="tdnf clean all"
PKG_LISTS="/var/cache/tdnf/*"
PKG_QUERY="rpm -q"
elif command -v yum > /dev/null; then
PKG_MANAGER="yum"
PKG_UPDATE="yum -y makecache"
PKG_INSTALL="yum -y install"
PKG_CLEAN="yum clean all"
PKG_LISTS="/var/cache/yum/*"
PKG_QUERY="rpm -q"
else
echo "No supported package manager found (apt-get, apk, dnf, microdnf, tdnf, yum)."
exit 1
fi
}

detect_package_manager

# Clean up
rm -rf /var/lib/apt/lists/*
rm -rf $PKG_LISTS

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.'
Expand Down Expand Up @@ -47,7 +99,12 @@ elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then
fi

architecture="$(uname -m)"
if [ "${architecture}" != "x86_64" ]; then
# Normalize arm64 to aarch64 for consistency
if [ "${architecture}" = "arm64" ]; then
architecture="aarch64"
fi

if [ "${architecture}" != "x86_64" ] && [ "${architecture}" != "aarch64" ]; then
echo "(!) Architecture $architecture unsupported"
exit 1
fi
Expand All @@ -66,15 +123,94 @@ updaterc() {

# Checks if packages are installed and installs them if not
check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
echo "Running apt-get update..."
apt-get update -y
for pkg in "$@"; do
if [ "$PKG_MANAGER" = "apt-get" ]; then
if ! dpkg -s "$pkg" > /dev/null 2>&1; then
if [ "$(find $PKG_LISTS | wc -l)" = "0" ]; then
echo "Running $PKG_UPDATE..."
eval "$PKG_UPDATE"
fi
eval "$PKG_INSTALL $pkg"
fi
elif [ "$PKG_MANAGER" = "apk" ]; then
if ! apk info -e "$pkg" > /dev/null 2>&1; then
echo "Running $PKG_UPDATE..."
eval "$PKG_UPDATE"
eval "$PKG_INSTALL $pkg"
fi
else
if ! rpm -q "$pkg" > /dev/null 2>&1; then
echo "Running $PKG_UPDATE..."
eval "$PKG_UPDATE"
eval "$PKG_INSTALL $pkg"
fi
fi
apt-get -y install --no-install-recommends "$@"
done
}

sudo_if() {
COMMAND="$*"
if [ "$(id -u)" -eq 0 ] && [ "$USERNAME" != "root" ]; then
if command -v runuser > /dev/null; then
runuser -l "$USERNAME" -c "$COMMAND"
elif command -v su > /dev/null; then
su - "$USERNAME" -c "$COMMAND"
elif command -v sudo > /dev/null; then
sudo -u "$USERNAME" -i bash -c "$COMMAND"
else
# Fallback: execute as root (not ideal but works in containers)
echo "Warning: No user switching command available, running as root"
eval "$COMMAND"
fi
else
eval "$COMMAND"
fi
}

install_user_package() {
PACKAGE="$1"
sudo_if "${CONDA_DIR}/bin/python3" -m pip install --user --upgrade "$PACKAGE"
}

run_as_user() {
local user="$1"
shift
local cmd="$*"

if command -v runuser > /dev/null; then
if [ "$PKG_MANAGER" = "apk" ]; then
runuser "$user" -c "$cmd"
else
runuser -l "$user" -c "$cmd"
fi
elif command -v su > /dev/null; then
if [ "$PKG_MANAGER" = "apk" ]; then
su "$user" -c "$cmd"
else
su --login -c "$cmd" "$user"
fi
elif command -v sudo > /dev/null; then
if [ "$PKG_MANAGER" = "apk" ]; then
sudo -u "$user" sh -c "$cmd"
else
sudo -u "$user" -i bash -c "$cmd"
fi
else
echo "Warning: No user switching command available, running as root"
eval "$cmd"
fi
}
# Set permissions for directories recursively
set_directory_permissions() {
local dir="$1"
for item in "$dir"/*; do
if [ -d "$item" ]; then
chmod g+s "$item"
set_directory_permissions "$item"
fi
done
}

# Install Conda if it's missing
if ! conda --version &> /dev/null ; then
if ! cat /etc/group | grep -e "^conda:" > /dev/null 2>&1; then
Expand All @@ -83,30 +219,66 @@ if ! conda --version &> /dev/null ; then
usermod -a -G conda "${USERNAME}"

# Install dependencies
check_packages wget ca-certificates
if [ "$PKG_MANAGER" = "apt-get" ]; then
check_packages wget ca-certificates libgtk-3-0
elif [ "$PKG_MANAGER" = "apk" ]; then
check_packages wget ca-certificates gtk+3.0
else
check_packages wget ca-certificates gtk3
fi

mkdir -p $CONDA_DIR

chown -R "${USERNAME}:conda" "${CONDA_DIR}"
chmod -R g+r+w "${CONDA_DIR}"

find "${CONDA_DIR}" -type d -print0 | xargs -n 1 -0 chmod g+s
chmod -R g+r+w "${CONDA_DIR}"

echo "Installing Anaconda..."

CONDA_VERSION=$VERSION
if [ "${VERSION}" = "latest" ] || [ "${VERSION}" = "lts" ]; then
CONDA_VERSION="2021.11"
CONDA_VERSION="2024.10-1"
fi

su --login -c "export http_proxy=${http_proxy:-} && export https_proxy=${https_proxy:-} \
&& wget -q https://repo.anaconda.com/archive/Anaconda3-${CONDA_VERSION}-Linux-x86_64.sh -O /tmp/anaconda-install.sh \
&& /bin/bash /tmp/anaconda-install.sh -u -b -p ${CONDA_DIR}" ${USERNAME} 2>&1
if [ "${architecture}" = "x86_64" ]; then
run_as_user "${USERNAME}" "export http_proxy=${http_proxy:-} && export https_proxy=${https_proxy:-} \
&& wget -q https://repo.anaconda.com/archive/Anaconda3-${CONDA_VERSION}-Linux-x86_64.sh -O /tmp/anaconda-install.sh \
&& /bin/bash /tmp/anaconda-install.sh -u -b -p ${CONDA_DIR}"
elif [ "${architecture}" = "aarch64" ]; then
run_as_user "${USERNAME}" "export http_proxy=${http_proxy:-} && export https_proxy=${https_proxy:-} \
&& wget -q https://repo.anaconda.com/archive/Anaconda3-${CONDA_VERSION}-Linux-aarch64.sh -O /tmp/anaconda-install.sh \
&& /bin/bash /tmp/anaconda-install.sh -u -b -p ${CONDA_DIR}"
fi

if [ "${VERSION}" = "latest" ] || [ "${VERSION}" = "lts" ]; then
PATH=$PATH:${CONDA_DIR}/bin
conda update -y conda
fi

rm /tmp/anaconda-install.sh
chown -R "${USERNAME}:conda" "${CONDA_DIR}"
chmod -R g+r+w "${CONDA_DIR}"

# Set setgid bit on all directories - use find+xargs if available, fallback to recursive function
if command -v find > /dev/null && command -v xargs > /dev/null; then
find "${CONDA_DIR}" -type d -print0 | xargs -n 1 -0 chmod g+s
else
# Fallback for systems without find or xargs
if [ -d "${CONDA_DIR}" ]; then
chmod g+s "${CONDA_DIR}"
set_directory_permissions "${CONDA_DIR}"
fi
fi

# Temporary fixes
# Due to https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-23491
install_user_package certifi
# Due to https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-0286 and https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-23931
install_user_package pyopenssl
install_user_package cryptography
# Due to https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-40897
install_user_package setuptools
install_user_package tornado

rm /tmp/anaconda-install.sh
updaterc "export CONDA_DIR=${CONDA_DIR}/bin"
fi

Expand Down Expand Up @@ -135,7 +307,8 @@ if [ -f "/etc/bash.bashrc" ]; then
echo "${notice_script}" | tee -a /etc/bash.bashrc
fi

# Clean up
rm -rf /var/lib/apt/lists/*
# Final clean up
eval "$PKG_CLEAN"
rm -rf $PKG_LISTS

echo "Done!"
31 changes: 31 additions & 0 deletions test/anaconda/install_anaconda_almalinux8.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/bash

set -e

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

# Definition specific tests
check "conda" conda --version
check "python" python --version
check "pylint" pylint --version
check "flake8" flake8 --version
check "autopep8" autopep8 --version
check "yapf" yapf --version
check "pydocstyle" pydocstyle --version
check "pycodestyle" pycodestyle --version
check "if conda-notice.txt exists" cat /usr/local/etc/vscode-dev-containers/conda-notice.txt

check "certifi" pip show certifi | grep Version
check "cryptography" pip show cryptography | grep Version
check "setuptools" pip show setuptools | grep Version
check "tornado" pip show tornado | grep Version

check "conda-update-conda" bash -c "conda update -y conda"
check "conda-install-tensorflow" bash -c "conda create --name test-env -c conda-forge --yes tensorflow"
check "conda-install-pytorch" bash -c "conda create --name test-env -c conda-forge --yes pytorch"

# Report result
reportResults


31 changes: 31 additions & 0 deletions test/anaconda/install_anaconda_almalinux9.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/bash

set -e

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

# Definition specific tests
check "conda" conda --version
check "python" python --version
check "pylint" pylint --version
check "flake8" flake8 --version
check "autopep8" autopep8 --version
check "yapf" yapf --version
check "pydocstyle" pydocstyle --version
check "pycodestyle" pycodestyle --version
check "if conda-notice.txt exists" cat /usr/local/etc/vscode-dev-containers/conda-notice.txt

check "certifi" pip show certifi | grep Version
check "cryptography" pip show cryptography | grep Version
check "setuptools" pip show setuptools | grep Version
check "tornado" pip show tornado | grep Version

check "conda-update-conda" bash -c "conda update -y conda"
check "conda-install-tensorflow" bash -c "conda create --name test-env -c conda-forge --yes tensorflow"
check "conda-install-pytorch" bash -c "conda create --name test-env -c conda-forge --yes pytorch"

# Report result
reportResults


30 changes: 30 additions & 0 deletions test/anaconda/install_anaconda_bookworm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash

set -e

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

# Definition specific tests
check "conda" conda --version
check "python" python --version
check "pylint" pylint --version
check "flake8" flake8 --version
check "autopep8" autopep8 --version
check "yapf" yapf --version
check "pydocstyle" pydocstyle --version
check "pycodestyle" pycodestyle --version
check "if conda-notice.txt exists" cat /usr/local/etc/vscode-dev-containers/conda-notice.txt

check "certifi" pip show certifi | grep Version
check "cryptography" pip show cryptography | grep Version
check "setuptools" pip show setuptools | grep Version
check "tornado" pip show tornado | grep Version

check "conda-update-conda" bash -c "conda update -y conda"
check "conda-install-tensorflow" bash -c "conda create --name test-env -c conda-forge --yes tensorflow"
check "conda-install-pytorch" bash -c "conda create --name test-env -c conda-forge --yes pytorch"

# Report result
reportResults

Loading