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.

392 lines
15 KiB

  1. #!/bin/bash
  2. ## Set NO_DOCKER_RESTART to prevent restarting docker after upgrade.
  3. ## Set ALLOW_DOCKER_CHANGE to allow change your docker version.
  4. ## Set TARGET_DOCKER_VERSION to force a version (use cautiously!)
  5. exname=${0##*/}
  6. if [ "$exname" == "bash" ]; then
  7. ## probably launched through ``charm``
  8. if [ -n "$CHARM_NAME" ]; then
  9. exname="charm/${CHARM_NAME}"
  10. else
  11. echo "Error: cannot determine script name" >&2
  12. echo " Please either run this script directly or through \`\`charm\`\`" >&2
  13. exit 1
  14. fi
  15. fi
  16. for bin in apt-get grep dpkg file lsb_release; do
  17. if ! type -p "$bin" >/dev/null; then
  18. echo "Error: \`\`$bin\`\` not found" >&2
  19. exit 1
  20. fi
  21. done
  22. STATIC_DOCKER_URL="https://download.docker.com/linux/static/stable/x86_64"
  23. distro=$(lsb_release -is)
  24. release=$(lsb_release -rs)
  25. unsupported=
  26. target_version=${TARGET_DOCKER_VERSION:-24}
  27. declare -A distro_supported_version=(
  28. [Debian]="9 10 11 12"
  29. [Ubuntu]="20.04 22.04"
  30. )
  31. if [ "$distro" == "Debian" ]; then
  32. release=${release%%.*}
  33. fi
  34. if [ -z "${distro_supported_version[$distro]}" ] ||
  35. [[ " ${distro_supported_version[$distro]} " != *" $release "* ]]; then
  36. echo "Warning: possibly unsupported distribution/version $distro $release" >&2
  37. echo " Supported distributions and versions:" >&2
  38. for dist in "${!distro_supported_version[@]}"; do
  39. echo -n " $dist: " >&2
  40. ## comma separated list
  41. versions="${distro_supported_version[$dist]}"
  42. versions="${versions// /, }"
  43. echo "$versions" >&2
  44. done
  45. unsupported=1
  46. fi
  47. version_gt() { test "$(echo -e "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; }
  48. just_installed=
  49. if ! docker_bin=$(type -p docker); then
  50. echo "Installing docker..."
  51. type -p curl >/dev/null ||
  52. apt-get install -y curl </dev/null
  53. curl -sSL https://get.docker.io | sh || exit 1
  54. just_installed=1
  55. fi
  56. is_static_docker() {
  57. local docker_bin=$1
  58. # check if this is a link
  59. [[ -L "$docker_bin" ]] && docker_bin=$(readlink -f "$docker_bin")
  60. ## XXXvlab: We need to handle a special case that happens on some systems
  61. ## having installed 0k-docker package that divert the docker
  62. ## binary to a shell script that then calls docker
  63. ## check if this a shell script (using file)
  64. if file "$docker_bin" | grep -q shell; then
  65. echo "INFO: using a shell script for docker in '$docker_bin'" >&2
  66. ## is the script mentioning "/usr/bin/docker" ?
  67. if grep -q '/usr/bin/docker' "$docker_bin"; then
  68. echo "INFO: docker script is calling '/usr/bin/docker'" >&2
  69. docker_bin=/usr/bin/docker
  70. else
  71. echo "ERROR: cannot determine docker binary from script '$docker_bin'" >&2
  72. return 1
  73. fi
  74. fi
  75. [[ "$(ldd "$docker_bin")" == *not\ a\ dynamic\ executable* ]]
  76. }
  77. is_static_docker_install=0
  78. if is_static_docker "$docker_bin"; then
  79. echo "INFO: docker version is static" >&2
  80. is_static_docker_install=1
  81. fi
  82. read docker_client_version docker_server_version < <(
  83. docker version --format '{{.Client.Version}} {{.Server.Version}}'
  84. )
  85. mismatch_client_server=
  86. ## issue a warning if client and server versions are different
  87. if [ "$docker_client_version" != "$docker_server_version" ]; then
  88. echo "WARNING: docker client and server versions differ!" >&2
  89. echo " client: $docker_client_version" >&2
  90. echo " server: $docker_server_version" >&2
  91. mismatch_client_server=1
  92. fi
  93. distrib_available_versions=$(apt-cache madison docker-ce | cut -f 2 -d "|" | sort -V)
  94. deb_version_to_docker_version() {
  95. local deb_version=$1
  96. local docker_version
  97. docker_version="${deb_version%%~*}"
  98. docker_version="${docker_version%-*}"
  99. docker_version="${docker_version#*:}"
  100. echo "$docker_version"
  101. }
  102. declare -A docker_version_to_deb_version=()
  103. for deb_version in $distrib_available_versions; do
  104. docker_version=$(deb_version_to_docker_version "$deb_version")
  105. docker_version_to_deb_version["$docker_version"]="$deb_version"
  106. done
  107. ## expand docker version to one that is available in deb on the repository
  108. candidate_deb_target_version=$(
  109. echo "${!docker_version_to_deb_version[@]}" |
  110. tr ' ' '\n' | grep "^${target_version}[^\d]" | sort -V | tail -n 1
  111. )
  112. if [ -z "$candidate_deb_target_version" ]; then
  113. ## No deb package seems to satisfy the target version
  114. # Fetch the HTML content and filter out the versions
  115. available_docker_static_versions=$(
  116. curl -sL "$STATIC_DOCKER_URL" | grep -oP 'docker-\d+\.\d+\.\d+\.tgz' |
  117. cut -d'-' -f2 | cut -d'.' -f1,2,3 | sort -uV
  118. )
  119. candidate_target_version=$(
  120. echo "$available_docker_static_versions" |
  121. grep "^${target_version}" | sort -V | tail -n 1
  122. )
  123. if [ -z "$candidate_target_version" ]; then
  124. echo "ERROR: no docker version matching '${target_version}' available" >&2
  125. echo " Available versions for $distro $release:" >&2
  126. all_versions=(
  127. $(
  128. {
  129. echo "${!docker_version_to_deb_version[@]}" | tr ' ' '\n'
  130. echo "$available_docker_static_versions"
  131. } | cut -f 1,2 -d. | sort -uV
  132. )
  133. )
  134. for version in "${all_versions[@]}"; do
  135. candidate_deb_target_version=$(
  136. echo "${!docker_version_to_deb_version[@]}" |
  137. tr ' ' '\n' | grep "^${version}[^\d]" | sort -V | tail -n 1
  138. )
  139. if [ -n "$candidate_deb_target_version" ]; then
  140. msg=" with deb"
  141. else
  142. msg=" with static version"
  143. fi
  144. printf " - %-10s %s\n" "$version" "$msg" >&2
  145. done
  146. echo >&2
  147. echo " 1) You may need to contact your system administrator to upgrade this script." >&2
  148. echo " 2) Meanwhile, if you know what you are doing, you can set yourself a target version" >&2
  149. echo " by setting TARGET_DOCKER_VERSION." >&2
  150. exit 1
  151. fi
  152. echo "INFO: target docker version $target_version is not supported by distrib" >&2
  153. echo " Binary static version $candidate_target_version of docker will be used" >&2
  154. max_docker_version_available=
  155. for docker_version in ${!docker_version_to_deb_version[@]}; do
  156. if version_gt "$docker_version" "$target_version"; then
  157. continue
  158. fi
  159. if version_gt "$docker_version" "$max_docker_version_available"; then
  160. max_docker_version_available="$docker_version"
  161. fi
  162. done
  163. if [ -z "$max_docker_version_available" ]; then
  164. ## use lowest version available
  165. echo "INFO: no inferior docker version available in deb repository" \
  166. "to satisfy target version $target_version" >&2
  167. max_docker_version_available=$(
  168. echo "${!docker_version_to_deb_version[@]}" |
  169. tr ' ' '\n' |
  170. sort -V |
  171. head -n 1
  172. )
  173. if [ -z "$max_docker_version_available" ]; then
  174. echo "ERROR: no docker version available in deb repository" >&2
  175. exit 1
  176. fi
  177. echo "INFO: using lowest docker version available in deb repository" >&2
  178. fi
  179. echo "INFO: closest docker version supported by $distro $release:" \
  180. "$(deb_version_to_docker_version "$max_docker_version_available")" >&2
  181. target_deb_version="${docker_version_to_deb_version[$max_docker_version_available]}"
  182. target_version=$candidate_target_version
  183. echo "INFO: target maximal deb version: '$target_deb_version' (docker version: $(
  184. deb_version_to_docker_version "$target_deb_version"
  185. ))" >&2
  186. need_static=1
  187. else
  188. ## a deb package is available to satisfy the target version
  189. target_deb_version="${docker_version_to_deb_version[$candidate_deb_target_version]}"
  190. target_version=$candidate_deb_target_version
  191. echo "INFO: target docker deb version: '$target_deb_version' " \
  192. "(docker version: $candidate_deb_target_version)" >&2
  193. need_static=0
  194. fi
  195. current_docker_ce_package_version=$(
  196. dpkg -s docker-ce | grep '^Version:' | cut -f 2 -d ' '
  197. )
  198. current_docker_ce_cli_package_version=$(
  199. dpkg -s docker-ce-cli | grep '^Version:' | cut -f 2 -d ' '
  200. )
  201. ## Install deb version if required
  202. if [ -z "$mismatch_client_server" ] &&
  203. [[ "$current_docker_ce_cli_package_version" == "$target_deb_version" ]] &&
  204. [[ "$current_docker_ce_package_version" == "$target_deb_version" ]]; then
  205. if [[ "$need_static" == 0 ]] && [[ "$is_static_docker_install" == 0 ]]; then
  206. ## current and target versions match, no upgrade needed
  207. echo "INFO: recommended target deb docker version $target_version already installed" >&2
  208. echo " To set a different target version, set TARGET_DOCKER_VERSION" >&2
  209. exit 0
  210. fi
  211. ## Check static version and current version matches the target_version
  212. if [[ "$need_static" == 1 ]] && [[ "$is_static_docker_install" == 1 ]] &&
  213. [[ "$target_version" == "$docker_server_version" ]]; then
  214. ## current and target versions match, no upgrade needed
  215. echo "INFO: recommended target deb and static docker version $target_version already installed" >&2
  216. echo " To set a different target version, set TARGET_DOCKER_VERSION" >&2
  217. exit 0
  218. fi
  219. fi
  220. ## Should we skip upgrade?
  221. if [ -z "$just_installed" ] && [ -z "$ALLOW_DOCKER_CHANGE" ]; then
  222. echo "WARN: You are not on the recommended setup (target docker version: $target_version)" >&2
  223. if [[ "$current_docker_ce_package_version" != "$target_deb_version" ]]; then
  224. echo "INFO: docker-ce recommended deb version available" >&2
  225. echo " current: $current_docker_ce_package_version" >&2
  226. echo " recommended: $target_deb_version" >&2
  227. fi
  228. if [[ "$current_docker_ce_cli_package_version" != "$target_deb_version" ]]; then
  229. echo "INFO: docker-ce-cli recommended deb version available" >&2
  230. echo " current: $current_docker_ce_cli_package_version" >&2
  231. echo " recommended: $target_deb_version" >&2
  232. fi
  233. if [[ "$need_static" != "$is_static_docker_install" ]]; then
  234. if [[ "$need_static" == 1 ]]; then
  235. echo "INFO: docker static version recommended to achieved version $target_version" >&2
  236. else
  237. echo "INFO: docker deb version recommended, fulfilled by deb version $target_deb_version" >&2
  238. fi
  239. fi
  240. echo "INFO: To change version, run this script with ALLOW_DOCKER_CHANGE=1" >&2
  241. exit 0
  242. fi
  243. install_static_version() {
  244. local docker_version="$1"
  245. local url="$STATIC_DOCKER_URL/docker-${docker_version}.tgz"
  246. local service
  247. ## ensure shadowlocal is set up
  248. if ! [ -e /etc/profile.d/shadowlocal.sh ]; then
  249. cat <<EOF > /etc/profile.d/shadowlocal.sh || return 1
  250. ## add /opt/merge/bin to PATH if not already there
  251. if [[ ":$PATH:" != *":/opt/merge/bin:"* ]]; then
  252. export PATH="/opt/merge/bin:\$PATH"
  253. fi
  254. EOF
  255. fi
  256. if ! [ -e /etc/systemd/system.conf.d/shadowlocal.sh ]; then
  257. mkdir -p /etc/systemd/system.conf.d
  258. cat <<EOF > /etc/systemd/system.conf.d/shadowlocal.sh || return 1
  259. [Manager]
  260. DefaultEnvironment=PATH=/opt/merge/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
  261. EOF
  262. systemctl daemon-reload
  263. fi
  264. ## install in /opt/apps/docker
  265. local base_install=/opt/apps/docker
  266. if [ -d "${base_install}" ]; then
  267. if [ -d "${base_install}".old ]; then
  268. rm -rfv "${base_install}".old || return 1
  269. fi
  270. mv -v "${base_install}"{,.old} || return 1
  271. fi
  272. echo "Fetching $url" >&2
  273. mkdir -p "${base_install}"/bin &&
  274. (
  275. set -o pipefail
  276. curl -sSL "$url" | tar -xz -C "${base_install}"/bin --strip-components=1
  277. ) || return 1
  278. for service in docker:dockerd containerd:containerd; do
  279. bin=${service#*:}
  280. service=${service%%:*}
  281. ## get the original ExecStart line
  282. if ! orig_exec_start=$(grep -m 1 '^ExecStart=' /lib/systemd/system/${service}.service); then
  283. echo "ERROR: cannot find ExecStart in /lib/systemd/system/${service}.service" >&2
  284. return 1
  285. fi
  286. ## override the ExecStart line by changing only the binary
  287. new_exec_start="$(echo "$orig_exec_start" | sed -r 's|ExecStart=[^ ]+|/opt/merge/bin/'"${bin}"'|')"
  288. mkdir -p "${base_install}/etc/systemd/system/${service}.service.d"
  289. mkdir -p "/etc/systemd/system/${service}.service.d"
  290. cat <<EOF > "${base_install}/etc/systemd/system/${service}.service.d/docker-static.conf"
  291. [Service]
  292. ExecStart=
  293. ExecStart=$new_exec_start
  294. EOF
  295. ## override systemd config
  296. ln -sf /opt/merge/etc/systemd/system/docker.service.d/docker-static.conf \
  297. /etc/systemd/system/docker.service.d/docker-static.conf
  298. done
  299. ## Create symlinks to /opt/merge/bin with /opt/apps/docker/bin/*
  300. shadowlocal add docker || return 1 ## requires kal-scripts
  301. if [ -z "$NO_DOCKER_RESTART" ]; then
  302. systemctl daemon-reload &&
  303. service containerd restart &&
  304. service docker restart
  305. fi
  306. }
  307. uninstall_static_version() {
  308. local base_install=/opt/apps/docker
  309. if [ -d "${base_install}" ]; then
  310. rm -rfv "${base_install}" || return 1
  311. fi
  312. find -L "/opt/merge/bin" -maxdepth 1 -type l -ilname "/opt/apps/docker"/\* -exec echo rm -v {} \; || return 1
  313. for service in docker containerd; do
  314. rm -vf "/etc/systemd/system/${service}.service.d/docker-static.conf" || return 1
  315. done
  316. }
  317. ## if currently static and it doesn't need it anymore, uninstall
  318. if [[ "$is_static_docker_install" == 1 ]] && [[ "$need_static" == 0 ]]; then
  319. echo "INFO: uninstalling static docker version" >&2
  320. uninstall_static_version || exit 1
  321. fi
  322. ## if current docker-ce debian version is not the target debian version, install it
  323. if [[ "$current_docker_ce_package_version" != "$target_deb_version" ]]; then
  324. echo "INFO: installing docker-ce deb version $target_deb_version" >&2
  325. apt-get install -y --allow-downgrades \
  326. docker-ce="$target_deb_version" </dev/null || exit 1
  327. if [ -z "$NO_DOCKER_RESTART" ]; then
  328. systemctl daemon-reload &&
  329. service containerd restart &&
  330. service docker restart
  331. fi
  332. fi
  333. ## if current docker-ce-cli debian version is not the target debian version, install it
  334. if [[ "$current_docker_ce_cli_package_version" != "$target_deb_version" ]]; then
  335. echo "INFO: installing docker-ce-cli deb version $target_deb_version" >&2
  336. apt-get install -y --allow-downgrades \
  337. docker-ce-cli="$target_deb_version" </dev/null || exit 1
  338. fi
  339. ## if we need a static version and it is not installed, install it
  340. ## and if we need a static version and it is installed but not the target version, install it
  341. if [[ "$need_static" == 1 ]] && [[ "$is_static_docker_install" == 0 || "$target_version" != "$docker_server_version" ]]; then
  342. echo "INFO: installing static docker version $target_version" >&2
  343. install_static_version "$target_version" || exit 1
  344. fi