this way after an ubuntu upgrade, we update it with the correct docker package on the next cloudron upgrade
210 lines
8.5 KiB
Bash
Executable File
210 lines
8.5 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# This script is run before the box code is switched. This means that we can
|
|
# put network related/curl downloads here. If the script fails, the old code
|
|
# will continue to run
|
|
|
|
set -eu -o pipefail
|
|
|
|
if [[ ${EUID} -ne 0 ]]; then
|
|
echo "This script should be run as root." > /dev/stderr
|
|
exit 1
|
|
fi
|
|
|
|
function log() {
|
|
echo -e "$(date +'%Y-%m-%dT%H:%M:%S')" "==> installer: $1"
|
|
}
|
|
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
|
|
apt_ready="no"
|
|
function prepare_apt_once() {
|
|
[[ "${apt_ready}" == "yes" ]] && return
|
|
|
|
log "Making sure apt is in a good state"
|
|
|
|
log "Waiting for all dpkg tasks to finish..."
|
|
while fuser /var/lib/dpkg/lock; do
|
|
sleep 1
|
|
done
|
|
|
|
# it's unclear what needs to be run first or whether both these command should be run. so keep trying both
|
|
for count in {1..3}; do
|
|
# alternative to apt-install -y --fix-missing ?
|
|
if ! dpkg --force-confold --configure -a; then
|
|
log "dpkg reconfigure failed (try $count)"
|
|
dpkg_configure="no"
|
|
else
|
|
dpkg_configure="yes"
|
|
fi
|
|
|
|
if ! apt update -y; then
|
|
log "apt update failed (try $count)"
|
|
apt_update="no"
|
|
else
|
|
apt_update="yes"
|
|
fi
|
|
|
|
[[ "${dpkg_configure}" == "yes" && "${apt_update}" == "yes" ]] && break
|
|
|
|
sleep 1
|
|
done
|
|
|
|
apt_ready="yes"
|
|
|
|
if [[ "${dpkg_configure}" == "yes" && "${apt_update}" == "yes" ]]; then
|
|
log "apt is ready"
|
|
else
|
|
log "apt is not ready but proceeding anyway"
|
|
fi
|
|
}
|
|
|
|
readonly user=yellowtent
|
|
readonly box_src_dir=/home/${user}/box
|
|
|
|
readonly curl="curl --fail --connect-timeout 20 --retry 10 --retry-delay 2 --max-time 2400"
|
|
readonly script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
readonly box_src_next_dir="$(realpath ${script_dir}/..)"
|
|
|
|
readonly ubuntu_version=$(lsb_release -rs)
|
|
readonly ubuntu_codename=$(lsb_release -cs)
|
|
|
|
readonly is_update=$(systemctl is-active -q box && echo "yes" || echo "no")
|
|
|
|
log "Updating from $(cat $box_src_dir/VERSION 2>/dev/null) to $(cat $box_src_next_dir/VERSION 2>/dev/null)"
|
|
|
|
if [[ "${ubuntu_version}" == "18.04" ]]; then
|
|
log "This Cloudron version requires atleast Ubuntu 20.04. Please upgrade following https://docs.cloudron.io/guides/upgrade-ubuntu-20/"
|
|
exit 2
|
|
fi
|
|
|
|
# switch over to resolved and uninstall resolvconf
|
|
if dpkg -s resolvconf 2>/dev/null >/dev/null; then
|
|
vendor=$(cat /sys/devices/virtual/dmi/id/sys_vendor || true)
|
|
|
|
prepare_apt_once # do this first before DNS goes away intermittently
|
|
|
|
log "disabling unbound"
|
|
systemctl disable unbound || true
|
|
systemctl stop unbound || true
|
|
|
|
log "enabling systemd-resolved"
|
|
systemctl enable --now systemd-resolved
|
|
|
|
log "removing resolvconf"
|
|
[[ "${vendor}" == "netcup" && -f /etc/resolvconf/resolv.conf.d/original ]] && cp /etc/resolvconf/resolv.conf.d/original /tmp/resolv.conf.original # stash before purging
|
|
apt -y --purge remove resolvconf # purge required for dpkg -s to return error code
|
|
|
|
if [[ "${vendor}" == "netcup" && -f /tmp/resolv.conf.original ]]; then
|
|
log "Fix netcup DNS setup"
|
|
nameservers=$(sed -ne 's/nameserver \(.*\)/"\1"/p' /tmp/resolv.conf.original | paste -sd "," -) # json array
|
|
netplan set --origin-hint 50-cloud-init "ethernets.eth0.nameservers.addresses=[${nameservers}]"
|
|
netplan apply # generates /run/systemd/resolve/resolv.conf
|
|
systemctl restart systemd-resolved
|
|
fi
|
|
fi
|
|
|
|
# https://docs.docker.com/engine/installation/linux/ubuntulinux/
|
|
# https://download.docker.com/linux/ubuntu/dists/noble/pool/stable/amd64/
|
|
# this is the highest on Ubuntu 20.04 focal
|
|
readonly docker_version="28.1.1"
|
|
readonly docker_deb_version="5:${docker_version}-1~ubuntu.${ubuntu_version}~${ubuntu_codename}"
|
|
readonly containerd_version="1.7.27"
|
|
if ! dpkg -s docker-ce >/dev/null 2>&1 || \
|
|
[[ "$(dpkg-query --show --showformat='${Version}' docker-ce 2>/dev/null)" != "${docker_deb_version}" ]]; then
|
|
log "installing/updating docker"
|
|
|
|
# create systemd drop-in file already to make sure images are with correct driver
|
|
mkdir -p /etc/systemd/system/docker.service.d
|
|
echo -e "[Service]\nExecStart=\nExecStart=/usr/bin/dockerd -H fd:// --log-driver=journald --exec-opt native.cgroupdriver=cgroupfs --storage-driver=overlay2 --experimental --ip6tables" > /etc/systemd/system/docker.service.d/cloudron.conf
|
|
|
|
# there are 3 packages for docker - containerd, CLI and the daemon (https://download.docker.com/linux/ubuntu/dists/jammy/pool/stable/amd64/)
|
|
$curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/containerd.io_${containerd_version}-1_amd64.deb" -o /tmp/containerd.deb
|
|
$curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/docker-ce-cli_${docker_version}-1~ubuntu.${ubuntu_version}~${ubuntu_codename}_amd64.deb" -o /tmp/docker-ce-cli.deb
|
|
$curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/docker-ce_${docker_version}-1~ubuntu.${ubuntu_version}~${ubuntu_codename}_amd64.deb" -o /tmp/docker.deb
|
|
|
|
log "installing docker"
|
|
prepare_apt_once
|
|
apt install -y /tmp/containerd.deb /tmp/docker-ce-cli.deb /tmp/docker.deb
|
|
rm /tmp/containerd.deb /tmp/docker-ce-cli.deb /tmp/docker.deb
|
|
fi
|
|
|
|
readonly old_node_version=22.20.0
|
|
readonly node_version=24.13.0
|
|
if ! which node 2>/dev/null || [[ "$(node --version)" != "v${node_version}" ]]; then
|
|
log "installing/updating node ${node_version}"
|
|
mkdir -p /usr/local/node-${node_version}
|
|
$curl -sL https://nodejs.org/dist/v${node_version}/node-v${node_version}-linux-x64.tar.gz -o /tmp/node.tar.gz
|
|
tar zxf /tmp/node.tar.gz --strip-components=1 -C /usr/local/node-${node_version}
|
|
rm /tmp/node.tar.gz
|
|
ln -sf /usr/local/node-${node_version}/bin/node /usr/bin/node
|
|
ln -sf /usr/local/node-${node_version}/bin/npm /usr/bin/npm
|
|
rm -rf /usr/local/node-${old_node_version}
|
|
fi
|
|
|
|
# note that rebuild requires the above node
|
|
for try in `seq 1 10`; do
|
|
# for reasons unknown, the dtrace package will fail. but rebuilding second time will work
|
|
|
|
# We need --unsafe-perm as we run as root and the folder is owned by root,
|
|
# however by default npm drops privileges for npm rebuild
|
|
# https://docs.npmjs.com/misc/config#unsafe-perm
|
|
if cd "${box_src_next_dir}" && npm rebuild --unsafe-perm; then break; fi
|
|
log "Failed to rebuild, trying again"
|
|
sleep 5
|
|
done
|
|
|
|
# update TLDs rules.json (https://github.com/thom4parisot/tld.js?tab=readme-ov-file#updating-the-tlds-list)
|
|
if ! node "${box_src_next_dir}/node_modules/tldjs/bin/update.js"; then
|
|
log "Failed to update PSL, continue anyway"
|
|
fi
|
|
|
|
if [[ ${try} -eq 10 ]]; then
|
|
log "npm rebuild failed, giving up"
|
|
exit 4
|
|
fi
|
|
|
|
log "downloading new addon images"
|
|
images=$(node -e "const i = require('${box_src_next_dir}/src/infra_version.js'); console.log(Object.keys(i.images).map(x => i.images[x]).join(' '));")
|
|
|
|
# docker hub only uses first 64 bits for ipv6 addressing. this causes many ipv6 rate limit errors
|
|
# https://www.docker.com/blog/beta-ipv6-support-on-docker-hub-registry/
|
|
log "\tPulling docker images: ${images}"
|
|
for image_ref in ${images}; do
|
|
ipv4_image_ref="${image_ref/registry.docker.com/registry.ipv4.docker.com}"
|
|
ipv6_image_ref="${image_ref/registry.docker.com/registry.ipv6.docker.com}"
|
|
|
|
# on some machines, ipv6 pull just hangs
|
|
while true; do
|
|
if timeout --kill-after=10s 300s docker pull "${ipv4_image_ref}"; then # this pulls the image untagged using the sha256 but doesn't tag it!
|
|
docker tag "${ipv4_image_ref}" "${image_ref%@sha256:*}" # this will tag the image for readability
|
|
docker rmi "${ipv4_image_ref}"
|
|
break
|
|
fi
|
|
log "Could not pull ${ipv4_image_ref} , trying IPv6"
|
|
if timeout --kill-after=10s 300s docker pull "${ipv6_image_ref}"; then # this pulls the image untagged using the sha256 but doesn't tag it!
|
|
docker tag "${ipv6_image_ref}" "${image_ref%@sha256:*}" # this will tag the image for readability
|
|
docker rmi "${ipv6_image_ref}"
|
|
break
|
|
fi
|
|
log "Could not pull ${ipv6_image_ref} either, waiting for 10s"
|
|
sleep 10
|
|
done
|
|
done
|
|
|
|
if [[ "${is_update}" == "yes" ]]; then
|
|
log "stop box service for update"
|
|
systemctl stop box
|
|
fi
|
|
|
|
# ensure we are not inside the source directory, which we will remove now
|
|
cd /root
|
|
|
|
log "switching the box code"
|
|
rm -rf "${box_src_dir}"
|
|
mv "${box_src_next_dir}" "${box_src_dir}"
|
|
chown -R "${user}:${user}" "${box_src_dir}"
|
|
|
|
log "calling box setup script"
|
|
"${box_src_dir}/setup/start.sh"
|