the idea (previously) was that the box code knew how to stop itself. this is why stop.sh of the _old_ code was invoked. we can just inline the code needed to stop the old version into installer.sh itself.
206 lines
8.2 KiB
Bash
Executable File
206 lines
8.2 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/
|
|
readonly docker_version="27.5.1"
|
|
readonly containerd_version="1.7.25"
|
|
if ! which docker 2>/dev/null || [[ $(docker version --format {{.Client.Version}}) != "${docker_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=20.18.0
|
|
readonly node_version=22.13.1
|
|
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}"
|
|
|
|
while true; do
|
|
if 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 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"
|