#!/bin/bash set -eu -o pipefail # change this to a hash when we make a upgrade release readonly LOG_FILE="/var/log/cloudron-setup.log" readonly MINIMUM_DISK_SIZE_GB="18" # this is the size of "/" and required to fit in docker images 18 is a safe bet for different reporting on 20GB min readonly MINIMUM_MEMORY="974" # this is mostly reported for 1GB main memory (DO 992, EC2 990, Linode 989, Serverdiscounter.com 974) readonly curl="curl --fail --connect-timeout 20 --retry 10 --retry-delay 2 --max-time 2400" # copied from cloudron-resize-fs.sh readonly rootfs_type=$(LC_ALL=C df --output=fstype / | tail -n1) readonly physical_memory=$(LC_ALL=C free -m | awk '/Mem:/ { print $2 }') readonly disk_size_bytes=$(LC_ALL=C df --output=size / | tail -n1) readonly disk_size_gb=$((${disk_size_bytes}/1024/1024)) readonly RED='\033[31m' readonly GREEN='\033[32m' readonly DONE='\033[m' # verify the system has minimum requirements met if [[ "${rootfs_type}" != "ext4" ]]; then echo "Error: Cloudron requires '/' to be ext4" # see #364 exit 1 fi if [[ "${physical_memory}" -lt "${MINIMUM_MEMORY}" ]]; then echo "Error: Cloudron requires atleast 1GB physical memory" exit 1 fi if [[ "${disk_size_gb}" -lt "${MINIMUM_DISK_SIZE_GB}" ]]; then echo "Error: Cloudron requires atleast 20GB disk space (Disk space on / is ${disk_size_gb}GB)" exit 1 fi if systemctl -q is-active box; then echo "Error: Cloudron is already installed. To reinstall, start afresh" exit 1 fi initBaseImage="true" provider="generic" requestedVersion="" apiServerOrigin="https://api.cloudron.io" webServerOrigin="https://cloudron.io" sourceTarballUrl="" rebootServer="true" setupToken="" args=$(getopt -o "" -l "help,skip-baseimage-init,provider:,version:,env:,skip-reboot,generate-setup-token" -n "$0" -- "$@") eval set -- "${args}" while true; do case "$1" in --help) echo "See https://docs.cloudron.io/installation/ on how to install Cloudron"; exit 0;; --provider) provider="$2"; shift 2;; --version) requestedVersion="$2"; shift 2;; --env) if [[ "$2" == "dev" ]]; then apiServerOrigin="https://api.dev.cloudron.io" webServerOrigin="https://dev.cloudron.io" elif [[ "$2" == "staging" ]]; then apiServerOrigin="https://api.staging.cloudron.io" webServerOrigin="https://staging.cloudron.io" fi shift 2;; --skip-baseimage-init) initBaseImage="false"; shift;; --skip-reboot) rebootServer="false"; shift;; --generate-setup-token) setupToken="$(openssl rand -hex 10)"; shift;; --) break;; *) echo "Unknown option $1"; exit 1;; esac done # Only --help works as non-root if [[ ${EUID} -ne 0 ]]; then echo "This script should be run as root." > /dev/stderr exit 1 fi # Only --help works with mismatched ubuntu ubuntu_version=$(lsb_release -rs) if [[ "${ubuntu_version}" != "16.04" && "${ubuntu_version}" != "18.04" && "${ubuntu_version}" != "20.04" ]]; then echo "Cloudron requires Ubuntu 16.04, 18.04 or 20.04" > /dev/stderr exit 1 fi # Can only write after we have confirmed script has root access echo "Running cloudron-setup with args : $@" > "${LOG_FILE}" echo "" echo "##############################################" echo " Cloudron Setup (${requestedVersion:-latest})" echo "##############################################" echo "" echo " Follow setup logs in a second terminal with:" echo " $ tail -f ${LOG_FILE}" echo "" echo " Join us at https://forum.cloudron.io for any questions." echo "" if [[ "${initBaseImage}" == "true" ]]; then echo "=> Installing software-properties-common" if ! apt-get install -y software-properties-common &>> "${LOG_FILE}"; then echo "Could not install software-properties-common (for add-apt-repository below). See ${LOG_FILE}" exit 1 fi echo "=> Updating apt and installing script dependencies" if ! apt-get update &>> "${LOG_FILE}"; then echo "Could not update package repositories. See ${LOG_FILE}" exit 1 fi if ! DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -y install curl python3 ubuntu-standard -y &>> "${LOG_FILE}"; then echo "Could not install setup dependencies (curl). See ${LOG_FILE}" exit 1 fi fi echo "=> Checking version" if ! releaseJson=$($curl -s "${apiServerOrigin}/api/v1/releases?boxVersion=${requestedVersion}"); then echo "Failed to get release information" exit 1 fi if [[ "$requestedVersion" == "" ]]; then version=$(echo "${releaseJson}" | python3 -c 'import json,sys;obj=json.load(sys.stdin);print(obj["version"])') else version="${requestedVersion}" fi if ! sourceTarballUrl=$(echo "${releaseJson}" | python3 -c 'import json,sys;obj=json.load(sys.stdin);print(obj["info"]["sourceTarballUrl"])'); then echo "No source code for version '${requestedVersion:-latest}'" exit 1 fi echo "=> Downloading version ${version} ..." box_src_tmp_dir=$(mktemp -dt box-src-XXXXXX) if ! $curl -sL "${sourceTarballUrl}" | tar -zxf - -C "${box_src_tmp_dir}"; then echo "Could not download source tarball. See ${LOG_FILE} for details" exit 1 fi if [[ "${initBaseImage}" == "true" ]]; then echo -n "=> Installing base dependencies and downloading docker images (this takes some time) ..." # initializeBaseUbuntuImage.sh args (provider, infraversion path) are only to support installation of pre 5.3 Cloudrons if ! /bin/bash "${box_src_tmp_dir}/baseimage/initializeBaseUbuntuImage.sh" "generic" "../src" &>> "${LOG_FILE}"; then echo "Init script failed. See ${LOG_FILE} for details" exit 1 fi echo "" fi # The provider flag is still used for marketplace images echo "=> Installing version ${version} (this takes some time) ..." mkdir -p /etc/cloudron echo "${provider}" > /etc/cloudron/PROVIDER [[ ! -z "${setupToken}" ]] && echo "${setupToken}" > /etc/cloudron/SETUP_TOKEN if ! /bin/bash "${box_src_tmp_dir}/scripts/installer.sh" &>> "${LOG_FILE}"; then echo "Failed to install cloudron. See ${LOG_FILE} for details" exit 1 fi mysql -uroot -ppassword -e "REPLACE INTO box.settings (name, value) VALUES ('api_server_origin', '${apiServerOrigin}');" 2>/dev/null mysql -uroot -ppassword -e "REPLACE INTO box.settings (name, value) VALUES ('web_server_origin', '${webServerOrigin}');" 2>/dev/null echo -n "=> Waiting for cloudron to be ready (this takes some time) ..." while true; do echo -n "." if status=$($curl -s -f "http://localhost:3000/api/v1/cloudron/status" 2>/dev/null); then break # we are up and running fi sleep 10 done if ! ip=$(curl -s --fail --connect-timeout 2 --max-time 2 https://api.cloudron.io/api/v1/helper/public_ip | sed -n -e 's/.*"ip": "\(.*\)"/\1/p'); then ip='' fi if [[ -z "${setupToken}" ]]; then url="https://${ip}" else url="https://${ip}/?setupToken=${setupToken}" fi echo -e "\n\n${GREEN}After reboot, visit ${url} and accept the self-signed certificate to finish setup.${DONE}\n" if [[ "${rebootServer}" == "true" ]]; then systemctl stop box mysql # sometimes mysql ends up having corrupt privilege tables read -p "The server has to be rebooted to apply all the settings. Reboot now ? [Y/n] " yn yn=${yn:-y} case $yn in [Yy]* ) systemctl reboot;; * ) exit;; esac fi