Unverified Commit c264b4e8 authored by Gaurav Saini's avatar Gaurav Saini Committed by GitHub

[python] - issue #1172 - provide switching versions using update-alternatives (#1260)

* [python] - issue #1172 - add version switching mechanism using update-alternatives

* removed commented code

* [python] - update-alternatives addition for ease in switching of python versions - complete

* changes for passing of other tests !!

* change for better positioning of code

* Added test case scenario for Update-Alternatives check

* added scenario for testing

* [alternatives] - added option for fedora, centos & rhel

* changed file name

* changes for update-alternatives test case proper functioning

* arrange in asc order for default value retention after testing ends
parent 8f42e094
......@@ -766,15 +766,19 @@ esac
check_packages ${REQUIRED_PKGS}
# Function to get the major version from a SemVer string
get_major_version() {
local version="$1"
echo "$version" | cut -d '.' -f 1
}
# Install Python from source if needed
if [ "${PYTHON_VERSION}" != "none" ]; then
if ! cat /etc/group | grep -e "^python:" > /dev/null 2>&1; then
groupadd -r python
fi
usermod -a -G python "${USERNAME}"
CURRENT_PATH="${PYTHON_INSTALL_PATH}/current"
install_python ${PYTHON_VERSION}
# Additional python versions to be installed but not be set as default.
......@@ -783,9 +787,27 @@ if [ "${PYTHON_VERSION}" != "none" ]; then
OLDIFS=$IFS
IFS=","
read -a additional_versions <<< "$ADDITIONAL_VERSIONS"
for version in "${additional_versions[@]}"; do
major_version=$(get_major_version ${VERSION})
if type apt-get > /dev/null 2>&1; then
# Debian/Ubuntu: Use update-alternatives
update-alternatives --install ${CURRENT_PATH} python${major_version} ${PYTHON_INSTALL_PATH}/${VERSION} $((${#additional_versions[@]}+1))
update-alternatives --set python${major_version} ${PYTHON_INSTALL_PATH}/${VERSION}
elif type dnf > /dev/null 2>&1 || type yum > /dev/null 2>&1 || type microdnf > /dev/null 2>&1; then
# Fedora/RHEL/CentOS: Use alternatives
alternatives --install ${CURRENT_PATH} python${major_version} ${PYTHON_INSTALL_PATH}/${VERSION} $((${#additional_versions[@]}+1))
alternatives --set python${major_version} ${PYTHON_INSTALL_PATH}/${VERSION}
fi
for i in "${!additional_versions[@]}"; do
version=${additional_versions[$i]}
OVERRIDE_DEFAULT_VERSION="false"
install_python $version
if type apt-get > /dev/null 2>&1; then
# Debian/Ubuntu: Use update-alternatives
update-alternatives --install ${CURRENT_PATH} python${major_version} ${PYTHON_INSTALL_PATH}/${VERSION} $((${i}+1))
elif type dnf > /dev/null 2>&1 || type yum > /dev/null 2>&1 || type microdnf > /dev/null 2>&1; then
# Fedora/RHEL/CentOS: Use alternatives
alternatives --install ${CURRENT_PATH} python${major_version} ${PYTHON_INSTALL_PATH}/${VERSION} $((${i}+1))
fi
done
INSTALL_PATH="${OLD_INSTALL_PATH}"
IFS=$OLDIFS
......
#!/bin/bash
set -e
# Optional: Import test library
source dev-container-features-test-lib
check "python version 3.11 installed as default" bash -c "python --version | grep 3.11"
check "python3 version 3.11 installed as default" bash -c "python3 --version | grep 3.11"
check "python version 3.10.5 installed" bash -c "ls -l /usr/local/python | grep 3.10.5"
check "python version 3.8 installed" bash -c "ls -l /usr/local/python | grep 3.8"
check "python version 3.9.13 installed" bash -c "ls -l /usr/local/python | grep 3.9.13"
# Check paths in settings
check "current symlink is correct" bash -c "which python | grep /usr/local/python/current/bin/python"
check "current symlink works" /usr/local/python/current/bin/python --version
# check alternatives command
check_version_switch() {
if type apt-get > /dev/null 2>&1; then
PYTHON_ALTERNATIVES=$(update-alternatives --query python3 | grep -E 'Alternative:|Priority:')
STYLE="debian"
elif type dnf > /dev/null 2>&1 || type yum > /dev/null 2>&1 || type microdnf > /dev/null 2>&1; then
PYTHON_ALTERNATIVES=$(alternatives --display python3 | grep " - priority")
STYLE="fedora"
else
echo "No supported package manager found."
exit 1
fi
AVAILABLE_VERSIONS=()
INDEX=1
echo "Available Python versions:"
if [ "${STYLE}" = "debian" ]; then
while read -r alt && read -r pri; do
PATH=${alt#Alternative: } # Extract only the path
PRIORITY=${pri#Priority: } # Extract only the priority number
TEMP_VERSIONS+=("${PRIORITY} ${PATH}")
echo "$INDEX) $PATH (Priority: $PRIORITY)"
((INDEX++))
done <<< "${PYTHON_ALTERNATIVES}"
elif [ "${STYLE}" = "fedora" ]; then
export PATH="/usr/bin:$PATH"
# Fedora/RHEL output: one line per alternative in the format:
while IFS= read -r line; do
# Split using " - priority " as a delimiter.
PATH=$(/usr/bin/awk -F' - priority ' '{print $1}' <<< "$line" | /usr/bin/xargs /bin/echo)
PRIORITY=$(/usr/bin/awk -F' - priority ' '{print $2}' <<< "$line" | /usr/bin/xargs /bin/echo)
TEMP_VERSIONS+=("${PRIORITY} ${PATH}")
echo "$INDEX) $PATH (Priority: $PRIORITY_VALUE)"
((INDEX++))
done <<< "${PYTHON_ALTERNATIVES}"
fi
export PATH="/usr/bin:$PATH"
# Sort by priority (numerically ascending)
IFS=$'\n' TEMP_VERSIONS=($(sort -n <<<"${TEMP_VERSIONS[*]}"))
unset IFS
# Populate AVAILABLE_VERSIONS from sorted data
AVAILABLE_VERSIONS=()
INDEX=1
echo -e "\nAvailable Python versions (Sorted in asc order of priority):"
for ENTRY in "${TEMP_VERSIONS[@]}"; do
PRIORITY=${ENTRY%% *} # Extract priority (first part before space)
PATH=${ENTRY#* } # Extract path (everything after first space)
AVAILABLE_VERSIONS+=("${PATH}")
echo "$INDEX) $PATH (Priority: $PRIORITY)"
((INDEX++))
done
echo -e "\nAvailable Versions Count: ${#AVAILABLE_VERSIONS[@]}"
# Ensure at least 4 alternatives exist
if [ "${#AVAILABLE_VERSIONS[@]}" -lt 4 ]; then
echo "Error: Less than 4 Python versions registered in update-alternatives."
exit 1
fi
export PATH="/usr/bin:$PATH"
echo -e "\nSwitching to different versions using update-alternatives --set command...\n"
for CHOICE in {1..4}; do
SELECTED_VERSION="${AVAILABLE_VERSIONS[$((CHOICE - 1))]}"
echo "Switching to: ${SELECTED_VERSION}"
if command -v apt-get > /dev/null 2>&1; then
/usr/bin/update-alternatives --set python3 ${SELECTED_VERSION}
elif command -v dnf > /dev/null 2>&1 || command -v yum > /dev/null 2>&1 || command -v microdnf > /dev/null 2>&1; then
/usr/sbin/alternatives --set python3 ${SELECTED_VERSION}
fi
# Verify the switch
echo "Python version after switch:"
/usr/local/python/current/bin/python3 --version
/bin/sleep 1
echo -e "\n"
done
echo -e "Update-Alternatives --display: \n"
if type apt-get > /dev/null 2>&1; then
/usr/bin/update-alternatives --display python3
elif type dnf > /dev/null 2>&1 || type yum > /dev/null 2>&1 || type microdnf > /dev/null 2>&1; then
/usr/sbin/alternatives --display python3
fi
}
check "Version Switch With Update_Alternatives" check_version_switch
\ No newline at end of file
......@@ -255,5 +255,25 @@
"enableShared": true
}
}
},
"update_alternatives_switchable_versions": {
"image": "ubuntu:focal",
"features": {
"python": {
"version": "3.11",
"installTools": true,
"additionalVersions": "3.8,3.9.13,3.10.5"
}
}
},
"alternatives_switchable_versions": {
"image": "fedora",
"features": {
"python": {
"version": "3.11",
"installTools": true,
"additionalVersions": "3.8,3.9.13,3.10.5"
}
}
}
}
\ No newline at end of file
#!/bin/bash
set -e
# Optional: Import test library
source dev-container-features-test-lib
check "python version 3.11 installed as default" bash -c "python --version | grep 3.11"
check "python3 version 3.11 installed as default" bash -c "python3 --version | grep 3.11"
check "python version 3.10.5 installed" bash -c "ls -l /usr/local/python | grep 3.10.5"
check "python version 3.8 installed" bash -c "ls -l /usr/local/python | grep 3.8"
check "python version 3.9.13 installed" bash -c "ls -l /usr/local/python | grep 3.9.13"
# Check paths in settings
check "current symlink is correct" bash -c "which python | grep /usr/local/python/current/bin/python"
check "current symlink works" /usr/local/python/current/bin/python --version
# check alternatives command
check_version_switch() {
if type apt-get > /dev/null 2>&1; then
PYTHON_ALTERNATIVES=$(update-alternatives --query python3 | grep -E 'Alternative:|Priority:')
STYLE="debian"
elif type dnf > /dev/null 2>&1 || type yum > /dev/null 2>&1 || type microdnf > /dev/null 2>&1; then
PYTHON_ALTERNATIVES=$(alternatives --display python3 | grep " - priority")
STYLE="fedora"
else
echo "No supported package manager found."
exit 1
fi
AVAILABLE_VERSIONS=()
INDEX=1
echo "Available Python versions:"
if [ "${STYLE}" = "debian" ]; then
while read -r alt && read -r pri; do
PATH=${alt#Alternative: } # Extract only the path
PRIORITY=${pri#Priority: } # Extract only the priority number
TEMP_VERSIONS+=("${PRIORITY} ${PATH}")
echo "$INDEX) $PATH (Priority: $PRIORITY)"
((INDEX++))
done <<< "${PYTHON_ALTERNATIVES}"
elif [ "${STYLE}" = "fedora" ]; then
export PATH="/usr/bin:$PATH"
# Fedora/RHEL output: one line per alternative in the format:
while IFS= read -r line; do
# Split using " - priority " as a delimiter.
PATH=$(/usr/bin/awk -F' - priority ' '{print $1}' <<< "$line" | /usr/bin/xargs /bin/echo)
PRIORITY=$(/usr/bin/awk -F' - priority ' '{print $2}' <<< "$line" | /usr/bin/xargs /bin/echo)
TEMP_VERSIONS+=("${PRIORITY} ${PATH}")
echo "$INDEX) $PATH (Priority: $PRIORITY_VALUE)"
((INDEX++))
done <<< "${PYTHON_ALTERNATIVES}"
fi
export PATH="/usr/bin:$PATH"
# Sort by priority (numerically ascending)
IFS=$'\n' TEMP_VERSIONS=($(sort -n <<<"${TEMP_VERSIONS[*]}"))
unset IFS
# Populate AVAILABLE_VERSIONS from sorted data
AVAILABLE_VERSIONS=()
INDEX=1
echo -e "\nAvailable Python versions (Sorted in asc order of priority):"
for ENTRY in "${TEMP_VERSIONS[@]}"; do
PRIORITY=${ENTRY%% *} # Extract priority (first part before space)
PATH=${ENTRY#* } # Extract path (everything after first space)
AVAILABLE_VERSIONS+=("${PATH}")
echo "$INDEX) $PATH (Priority: $PRIORITY)"
((INDEX++))
done
echo -e "\nAvailable Versions Count: ${#AVAILABLE_VERSIONS[@]}\n"
# Ensure at least 4 alternatives exist
if [ "${#AVAILABLE_VERSIONS[@]}" -lt 4 ]; then
echo "Error: Less than 4 Python versions registered in update-alternatives."
exit 1
fi
export PATH="/usr/bin:$PATH"
echo -e "\nSwitching to different versions using update-alternatives --set command...\n"
for CHOICE in {1..4}; do
SELECTED_VERSION="${AVAILABLE_VERSIONS[$((CHOICE - 1))]}"
echo "Switching to: ${SELECTED_VERSION}"
if command -v apt-get > /dev/null 2>&1; then
/usr/bin/update-alternatives --set python3 ${SELECTED_VERSION}
elif command -v dnf > /dev/null 2>&1 || command -v yum > /dev/null 2>&1 || command -v microdnf > /dev/null 2>&1; then
/usr/sbin/alternatives --set python3 ${SELECTED_VERSION}
fi
# Verify the switch
echo "Python version after switch:"
/usr/local/python/current/bin/python3 --version
/bin/sleep 1
echo -e "\n"
done
echo -e "Update-Alternatives --display: \n"
if type apt-get > /dev/null 2>&1; then
/usr/bin/update-alternatives --display python3
elif type dnf > /dev/null 2>&1 || type yum > /dev/null 2>&1 || type microdnf > /dev/null 2>&1; then
/usr/sbin/alternatives --display python3
fi
}
check "Version Switch With Update_Alternatives" check_version_switch
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment