You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

396 lines
15 KiB

#!/bin/bash
## Set NO_DOCKER_RESTART to prevent restarting docker after upgrade.
## Set ALLOW_DOCKER_CHANGE to allow change your docker version.
## Set TARGET_DOCKER_VERSION to force a version (use cautiously!)
exname=${0##*/}
if [ "$exname" == "bash" ]; then
## probably launched through ``charm``
if [ -n "$CHARM_NAME" ]; then
exname="charm/${CHARM_NAME}"
else
echo "Error: cannot determine script name" >&2
echo " Please either run this script directly or through \`\`charm\`\`" >&2
exit 1
fi
fi
for bin in apt-get grep dpkg file lsb_release yq; do
if ! type -p "$bin" >/dev/null; then
echo "Error: \`\`$bin\`\` not found" >&2
exit 1
fi
done
##
## Install docker
##
STATIC_DOCKER_URL="https://download.docker.com/linux/static/stable/x86_64"
distro=$(lsb_release -is)
release=$(lsb_release -rs)
unsupported=
target_version=${TARGET_DOCKER_VERSION:-24}
declare -A distro_supported_version=(
[Debian]="9 10 11 12"
[Ubuntu]="20.04 22.04"
)
if [ "$distro" == "Debian" ]; then
release=${release%%.*}
fi
if [ -z "${distro_supported_version[$distro]}" ] ||
[[ " ${distro_supported_version[$distro]} " != *" $release "* ]]; then
echo "Warning: possibly unsupported distribution/version $distro $release" >&2
echo " Supported distributions and versions:" >&2
for dist in "${!distro_supported_version[@]}"; do
echo -n " $dist: " >&2
## comma separated list
versions="${distro_supported_version[$dist]}"
versions="${versions// /, }"
echo "$versions" >&2
done
unsupported=1
fi
version_gt() { test "$(echo -e "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; }
just_installed=
if ! docker_bin=$(type -p docker); then
echo "Installing docker..."
type -p curl >/dev/null ||
apt-get install -y curl </dev/null
curl -sSL https://get.docker.io | sh || exit 1
just_installed=1
fi
is_static_docker() {
local docker_bin=$1
# check if this is a link
[[ -L "$docker_bin" ]] && docker_bin=$(readlink -f "$docker_bin")
## XXXvlab: We need to handle a special case that happens on some systems
## having installed 0k-docker package that divert the docker
## binary to a shell script that then calls docker
## check if this a shell script (using file)
if file "$docker_bin" | grep -q shell; then
echo "INFO: using a shell script for docker in '$docker_bin'" >&2
## is the script mentioning "/usr/bin/docker" ?
if grep -q '/usr/bin/docker' "$docker_bin"; then
echo "INFO: docker script is calling '/usr/bin/docker'" >&2
docker_bin=/usr/bin/docker
else
echo "ERROR: cannot determine docker binary from script '$docker_bin'" >&2
return 1
fi
fi
[[ "$(ldd "$docker_bin")" == *not\ a\ dynamic\ executable* ]]
}
is_static_docker_install=0
if is_static_docker "$docker_bin"; then
echo "INFO: docker version is static" >&2
is_static_docker_install=1
fi
read docker_client_version docker_server_version < <(
docker version --format '{{.Client.Version}} {{.Server.Version}}'
)
mismatch_client_server=
## issue a warning if client and server versions are different
if [ "$docker_client_version" != "$docker_server_version" ]; then
echo "WARNING: docker client and server versions differ!" >&2
echo " client: $docker_client_version" >&2
echo " server: $docker_server_version" >&2
mismatch_client_server=1
fi
distrib_available_versions=$(apt-cache madison docker-ce | cut -f 2 -d "|" | sort -V)
deb_version_to_docker_version() {
local deb_version=$1
local docker_version
docker_version="${deb_version%%~*}"
docker_version="${docker_version%-*}"
docker_version="${docker_version#*:}"
echo "$docker_version"
}
declare -A docker_version_to_deb_version=()
for deb_version in $distrib_available_versions; do
docker_version=$(deb_version_to_docker_version "$deb_version")
docker_version_to_deb_version["$docker_version"]="$deb_version"
done
## expand docker version to one that is available in deb on the repository
candidate_deb_target_version=$(
echo "${!docker_version_to_deb_version[@]}" |
tr ' ' '\n' | grep "^${target_version}[^\d]" | sort -V | tail -n 1
)
if [ -z "$candidate_deb_target_version" ]; then
## No deb package seems to satisfy the target version
# Fetch the HTML content and filter out the versions
available_docker_static_versions=$(
curl -sL "$STATIC_DOCKER_URL" | grep -oP 'docker-\d+\.\d+\.\d+\.tgz' |
cut -d'-' -f2 | cut -d'.' -f1,2,3 | sort -uV
)
candidate_target_version=$(
echo "$available_docker_static_versions" |
grep "^${target_version}" | sort -V | tail -n 1
)
if [ -z "$candidate_target_version" ]; then
echo "ERROR: no docker version matching '${target_version}' available" >&2
echo " Available versions for $distro $release:" >&2
all_versions=(
$(
{
echo "${!docker_version_to_deb_version[@]}" | tr ' ' '\n'
echo "$available_docker_static_versions"
} | cut -f 1,2 -d. | sort -uV
)
)
for version in "${all_versions[@]}"; do
candidate_deb_target_version=$(
echo "${!docker_version_to_deb_version[@]}" |
tr ' ' '\n' | grep "^${version}[^\d]" | sort -V | tail -n 1
)
if [ -n "$candidate_deb_target_version" ]; then
msg=" with deb"
else
msg=" with static version"
fi
printf " - %-10s %s\n" "$version" "$msg" >&2
done
echo >&2
echo " 1) You may need to contact your system administrator to upgrade this script." >&2
echo " 2) Meanwhile, if you know what you are doing, you can set yourself a target version" >&2
echo " by setting TARGET_DOCKER_VERSION." >&2
exit 1
fi
echo "INFO: target docker version $target_version is not supported by distrib" >&2
echo " Binary static version $candidate_target_version of docker will be used" >&2
max_docker_version_available=
for docker_version in ${!docker_version_to_deb_version[@]}; do
if version_gt "$docker_version" "$target_version"; then
continue
fi
if version_gt "$docker_version" "$max_docker_version_available"; then
max_docker_version_available="$docker_version"
fi
done
if [ -z "$max_docker_version_available" ]; then
## use lowest version available
echo "INFO: no inferior docker version available in deb repository" \
"to satisfy target version $target_version" >&2
max_docker_version_available=$(
echo "${!docker_version_to_deb_version[@]}" |
tr ' ' '\n' |
sort -V |
head -n 1
)
if [ -z "$max_docker_version_available" ]; then
echo "ERROR: no docker version available in deb repository" >&2
exit 1
fi
echo "INFO: using lowest docker version available in deb repository" >&2
fi
echo "INFO: closest docker version supported by $distro $release:" \
"$(deb_version_to_docker_version "$max_docker_version_available")" >&2
target_deb_version="${docker_version_to_deb_version[$max_docker_version_available]}"
target_version=$candidate_target_version
echo "INFO: target maximal deb version: '$target_deb_version' (docker version: $(
deb_version_to_docker_version "$target_deb_version"
))" >&2
need_static=1
else
## a deb package is available to satisfy the target version
target_deb_version="${docker_version_to_deb_version[$candidate_deb_target_version]}"
target_version=$candidate_deb_target_version
echo "INFO: target docker deb version: '$target_deb_version' " \
"(docker version: $candidate_deb_target_version)" >&2
need_static=0
fi
current_docker_ce_package_version=$(
dpkg -s docker-ce | grep '^Version:' | cut -f 2 -d ' '
)
current_docker_ce_cli_package_version=$(
dpkg -s docker-ce-cli | grep '^Version:' | cut -f 2 -d ' '
)
## Install deb version if required
if [ -z "$mismatch_client_server" ] &&
[[ "$current_docker_ce_cli_package_version" == "$target_deb_version" ]] &&
[[ "$current_docker_ce_package_version" == "$target_deb_version" ]]; then
if [[ "$need_static" == 0 ]] && [[ "$is_static_docker_install" == 0 ]]; then
## current and target versions match, no upgrade needed
echo "INFO: recommended target deb docker version $target_version already installed" >&2
echo " To set a different target version, set TARGET_DOCKER_VERSION" >&2
exit 0
fi
## Check static version and current version matches the target_version
if [[ "$need_static" == 1 ]] && [[ "$is_static_docker_install" == 1 ]] &&
[[ "$target_version" == "$docker_server_version" ]]; then
## current and target versions match, no upgrade needed
echo "INFO: recommended target deb and static docker version $target_version already installed" >&2
echo " To set a different target version, set TARGET_DOCKER_VERSION" >&2
exit 0
fi
fi
## Should we skip upgrade?
if [ -z "$just_installed" ] && [ -z "$ALLOW_DOCKER_CHANGE" ]; then
echo "WARN: You are not on the recommended setup (target docker version: $target_version)" >&2
if [[ "$current_docker_ce_package_version" != "$target_deb_version" ]]; then
echo "INFO: docker-ce recommended deb version available" >&2
echo " current: $current_docker_ce_package_version" >&2
echo " recommended: $target_deb_version" >&2
fi
if [[ "$current_docker_ce_cli_package_version" != "$target_deb_version" ]]; then
echo "INFO: docker-ce-cli recommended deb version available" >&2
echo " current: $current_docker_ce_cli_package_version" >&2
echo " recommended: $target_deb_version" >&2
fi
if [[ "$need_static" != "$is_static_docker_install" ]]; then
if [[ "$need_static" == 1 ]]; then
echo "INFO: docker static version recommended to achieved version $target_version" >&2
else
echo "INFO: docker deb version recommended, fulfilled by deb version $target_deb_version" >&2
fi
fi
echo "INFO: To change version, run this script with ALLOW_DOCKER_CHANGE=1" >&2
exit 0
fi
install_static_version() {
local docker_version="$1"
local url="$STATIC_DOCKER_URL/docker-${docker_version}.tgz"
local service
## ensure shadowlocal is set up
if ! [ -e /etc/profile.d/shadowlocal.sh ]; then
cat <<EOF > /etc/profile.d/shadowlocal.sh || return 1
## add /opt/merge/bin to PATH if not already there
if [[ ":$PATH:" != *":/opt/merge/bin:"* ]]; then
export PATH="/opt/merge/bin:\$PATH"
fi
EOF
fi
if ! [ -e /etc/systemd/system.conf.d/shadowlocal.sh ]; then
mkdir -p /etc/systemd/system.conf.d
cat <<EOF > /etc/systemd/system.conf.d/shadowlocal.sh || return 1
[Manager]
DefaultEnvironment=PATH=/opt/merge/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
EOF
systemctl daemon-reload
fi
## install in /opt/apps/docker
local base_install=/opt/apps/docker
if [ -d "${base_install}" ]; then
if [ -d "${base_install}".old ]; then
rm -rfv "${base_install}".old || return 1
fi
mv -v "${base_install}"{,.old} || return 1
fi
echo "Fetching $url" >&2
mkdir -p "${base_install}"/bin &&
(
set -o pipefail
curl -sSL "$url" | tar -xz -C "${base_install}"/bin --strip-components=1
) || return 1
for service in docker:dockerd containerd:containerd; do
bin=${service#*:}
service=${service%%:*}
## get the original ExecStart line
if ! orig_exec_start=$(grep -m 1 '^ExecStart=' /lib/systemd/system/${service}.service); then
echo "ERROR: cannot find ExecStart in /lib/systemd/system/${service}.service" >&2
return 1
fi
## override the ExecStart line by changing only the binary
new_exec_start="$(echo "$orig_exec_start" | sed -r 's|ExecStart=[^ ]+|/opt/merge/bin/'"${bin}"'|')"
mkdir -p "${base_install}/etc/systemd/system/${service}.service.d"
mkdir -p "/etc/systemd/system/${service}.service.d"
cat <<EOF > "${base_install}/etc/systemd/system/${service}.service.d/docker-static.conf"
[Service]
ExecStart=
ExecStart=$new_exec_start
EOF
## override systemd config
ln -sf /opt/merge/etc/systemd/system/docker.service.d/docker-static.conf \
/etc/systemd/system/docker.service.d/docker-static.conf
done
## Create symlinks to /opt/merge/bin with /opt/apps/docker/bin/*
shadowlocal add docker || return 1 ## requires kal-scripts
if [ -z "$NO_DOCKER_RESTART" ]; then
systemctl daemon-reload &&
service containerd restart &&
service docker restart
fi
}
uninstall_static_version() {
local base_install=/opt/apps/docker
if [ -d "${base_install}" ]; then
rm -rfv "${base_install}" || return 1
fi
find -L "/opt/merge/bin" -maxdepth 1 -type l -ilname "/opt/apps/docker"/\* -exec echo rm -v {} \; || return 1
for service in docker containerd; do
rm -vf "/etc/systemd/system/${service}.service.d/docker-static.conf" || return 1
done
}
## if currently static and it doesn't need it anymore, uninstall
if [[ "$is_static_docker_install" == 1 ]] && [[ "$need_static" == 0 ]]; then
echo "INFO: uninstalling static docker version" >&2
uninstall_static_version || exit 1
fi
## if current docker-ce debian version is not the target debian version, install it
if [[ "$current_docker_ce_package_version" != "$target_deb_version" ]]; then
echo "INFO: installing docker-ce deb version $target_deb_version" >&2
apt-get install -y --allow-downgrades \
docker-ce="$target_deb_version" </dev/null || exit 1
if [ -z "$NO_DOCKER_RESTART" ]; then
systemctl daemon-reload &&
service containerd restart &&
service docker restart
fi
fi
## if current docker-ce-cli debian version is not the target debian version, install it
if [[ "$current_docker_ce_cli_package_version" != "$target_deb_version" ]]; then
echo "INFO: installing docker-ce-cli deb version $target_deb_version" >&2
apt-get install -y --allow-downgrades \
docker-ce-cli="$target_deb_version" </dev/null || exit 1
fi
## if we need a static version and it is not installed, install it
## and if we need a static version and it is installed but not the target version, install it
if [[ "$need_static" == 1 ]] && [[ "$is_static_docker_install" == 0 || "$target_version" != "$docker_server_version" ]]; then
echo "INFO: installing static docker version $target_version" >&2
install_static_version "$target_version" || exit 1
fi