#!/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" # provisioning data provider="" edition="" requestedVersion="" apiServerOrigin="https://api.cloudron.io" webServerOrigin="https://cloudron.io" sourceTarballUrl="" rebootServer="true" args=$(getopt -o "" -l "help,skip-baseimage-init,provider:,version:,env:,edition:,skip-reboot" -n "$0" -- "$@") eval set -- "${args}" while true; do case "$1" in --help) echo "See https://cloudron.io/documentation/installation/ on how to install Cloudron"; exit 0;; --provider) provider="$2"; shift 2;; --edition) edition="$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;; --) 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" ]]; then echo "Cloudron requires Ubuntu 16.04 or 18.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}" # validate arguments in the absence of data if [[ -z "${provider}" ]]; then echo "--provider is required (azure, contabo, digitalocean, ec2, exoscale, galaxygate, gce, hetzner, lightsail, linode, netcup, ovh, rosehosting, scaleway, upcloud, vultr or generic)" exit 1 elif [[ \ "${provider}" != "ami" && \ "${provider}" != "azure" && \ "${provider}" != "caas" && \ "${provider}" != "cloudscale" && \ "${provider}" != "contabo" && \ "${provider}" != "digitalocean" && \ "${provider}" != "digitalocean-mp" && \ "${provider}" != "ec2" && \ "${provider}" != "exoscale" && \ "${provider}" != "galaxygate" && \ "${provider}" != "digitalocean" && \ "${provider}" != "gce" && \ "${provider}" != "hetzner" && \ "${provider}" != "lightsail" && \ "${provider}" != "linode" && \ "${provider}" != "linode-stackscript" && \ "${provider}" != "netcup" && \ "${provider}" != "netcup-image" && \ "${provider}" != "ovh" && \ "${provider}" != "rosehosting" && \ "${provider}" != "scaleway" && \ "${provider}" != "upcloud" && \ "${provider}" != "upcloud-image" && \ "${provider}" != "vultr" && \ "${provider}" != "generic" \ ]]; then echo "--provider must be one of: azure, cloudscale.ch, contabo, digitalocean, ec2, exoscale, galaxygate, gce, hetzner, lightsail, linode, netcup, ovh, rosehosting, scaleway, upcloud, vultr or generic" exit 1 fi if [[ -n "${edition}" && ! -f "LICENSE" ]]; then echo "A LICENSE is required to use this edition. Please contact support@cloudron.io" exit 1 fi 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 "=> Ensure required apt sources" if ! add-apt-repository universe &>> "${LOG_FILE}"; then echo "Could not add required apt sources (for nginx-full). 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 ! apt-get 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) ..." if ! /bin/bash "${box_src_tmp_dir}/baseimage/initializeBaseUbuntuImage.sh" "${provider}" "../src" &>> "${LOG_FILE}"; then echo "Init script failed. See ${LOG_FILE} for details" exit 1 fi echo "" fi # NOTE: this install script only supports 3.x and above echo "=> Installing version ${version} (this takes some time) ..." if [[ "${version}" =~ 3\.[0-2]+\.[0-9]+ ]]; then readonly DATA_FILE="/root/cloudron-install-data.json" data=$(cat < "${DATA_FILE}" if ! /bin/bash "${box_src_tmp_dir}/scripts/installer.sh" --data-file "${DATA_FILE}" &>> "${LOG_FILE}"; then echo "Failed to install cloudron. See ${LOG_FILE} for details" exit 1 fi rm "${DATA_FILE}" else mkdir -p /etc/cloudron cat > "/etc/cloudron/cloudron.conf" <> "${LOG_FILE}"; then echo "Failed to install cloudron. See ${LOG_FILE} for details" exit 1 fi fi [[ -f LICENSE ]] && cp LICENSE /etc/cloudron/LICENSE echo -n "=> Waiting for cloudron to be ready (this takes some time) ..." while true; do echo -n "." if status=$($curl -q -f "http://localhost:3000/api/v1/cloudron/status" 2>/dev/null); then break # we are up and running fi sleep 10 done echo -e "\n\n${GREEN}Visit https:// 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