some disk types do not contain proper partition tables like on time4vps the type is simfs. On those fdisk fails to access the partition table, thus being unable to determine the size of the volume. df does only return the real usable disk space by the user, thus we lower the 20GB threshold to 18 Fixes #275
261 lines
9.1 KiB
Bash
Executable File
261 lines
9.1 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
set -eu -o pipefail
|
|
|
|
if [[ ${EUID} -ne 0 ]]; then
|
|
echo "This script should be run as root." > /dev/stderr
|
|
exit 1
|
|
fi
|
|
|
|
if [[ $(lsb_release -rs) != "16.04" ]]; then
|
|
echo "Cloudron requires Ubuntu 16.04" > /dev/stderr
|
|
exit 1
|
|
fi
|
|
|
|
# change this to a hash when we make a upgrade release
|
|
readonly LOG_FILE="/var/log/cloudron-setup.log"
|
|
readonly DATA_FILE="/root/cloudron-install-data.json"
|
|
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 physical_memory=$(LC_ALL=C free -m | awk '/Mem:/ { print $2 }')
|
|
readonly disk_device="$(for d in $(find /dev -type b); do [ "$(mountpoint -d /)" = "$(mountpoint -x $d)" ] && echo $d && break; done)"
|
|
readonly disk_size_bytes=$(LC_ALL=C df | grep "${disk_device}" | awk '{ printf $2 }')
|
|
readonly disk_size_gb=$((${disk_size_bytes}/1024/1024))
|
|
|
|
# verify the system has minimum requirements met
|
|
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 ${disk_device} is ${disk_size_gb}GB)"
|
|
exit 1
|
|
fi
|
|
|
|
initBaseImage="true"
|
|
# provisioning data
|
|
domain=""
|
|
provider=""
|
|
encryptionKey=""
|
|
restoreUrl=""
|
|
dnsProvider="manual"
|
|
tlsProvider="le-prod"
|
|
versionsUrl="https://s3.amazonaws.com/prod-cloudron-releases/versions.json"
|
|
requestedVersion="latest"
|
|
apiServerOrigin="https://api.cloudron.io"
|
|
dataJson=""
|
|
prerelease="false"
|
|
sourceTarballUrl=""
|
|
rebootServer="true"
|
|
|
|
args=$(getopt -o "" -l "domain:,help,skip-baseimage-init,data:,provider:,encryption-key:,restore-url:,tls-provider:,version:,versions-url:,api-server:,dns-provider:,env:,prerelease,skip-reboot,source-url:" -n "$0" -- "$@")
|
|
eval set -- "${args}"
|
|
|
|
while true; do
|
|
case "$1" in
|
|
--domain) domain="$2"; shift 2;;
|
|
--help) echo "See https://cloudron.io/references/selfhosting.html on how to install Cloudron"; exit 0;;
|
|
--provider) provider="$2"; shift 2;;
|
|
--encryption-key) encryptionKey="$2"; shift 2;;
|
|
--restore-url) restoreUrl="$2"; shift 2;;
|
|
--tls-provider) tlsProvider="$2"; shift 2;;
|
|
--dns-provider) dnsProvider="$2"; shift 2;;
|
|
--version) requestedVersion="$2"; shift 2;;
|
|
--env)
|
|
if [[ "$2" == "dev" ]]; then
|
|
apiServerOrigin="https://api.dev.cloudron.io"
|
|
versionsUrl="https://s3.amazonaws.com/dev-cloudron-releases/versions.json"
|
|
tlsProvider="le-staging"
|
|
prerelease="true"
|
|
elif [[ "$2" == "staging" ]]; then
|
|
apiServerOrigin="https://api.staging.cloudron.io"
|
|
versionsUrl="https://s3.amazonaws.com/staging-cloudron-releases/versions.json"
|
|
tlsProvider="le-staging"
|
|
prerelease="true"
|
|
fi
|
|
shift 2;;
|
|
--versions-url) versionsUrl="$2"; shift 2;;
|
|
--api-server) apiServerOrigin="$2"; shift 2;;
|
|
--skip-baseimage-init) initBaseImage="false"; shift;;
|
|
--skip-reboot) rebootServer="false"; shift;;
|
|
--data) dataJson="$2"; shift 2;;
|
|
--prerelease) prerelease="true"; shift;;
|
|
--source-url) sourceTarballUrl="$2"; version="0.0.1+custom"; shift 2;;
|
|
--) break;;
|
|
*) echo "Unknown option $1"; exit 1;;
|
|
esac
|
|
done
|
|
|
|
# validate arguments in the absence of data
|
|
if [[ -z "${dataJson}" ]]; then
|
|
if [[ -z "${provider}" ]]; then
|
|
echo "--provider is required (azure, digitalocean, ec2, lightsail, linode, ovh, scaleway, vultr or generic)"
|
|
exit 1
|
|
elif [[ \
|
|
"${provider}" != "ami" && \
|
|
"${provider}" != "azure" && \
|
|
"${provider}" != "digitalocean" && \
|
|
"${provider}" != "ec2" && \
|
|
"${provider}" != "lightsail" && \
|
|
"${provider}" != "linode" && \
|
|
"${provider}" != "ovh" && \
|
|
"${provider}" != "rosehosting" && \
|
|
"${provider}" != "scaleway" && \
|
|
"${provider}" != "vultr" && \
|
|
"${provider}" != "generic" \
|
|
]]; then
|
|
echo "--provider must be one of: azure, digitalocean, ec2, lightsail, linode, ovh, rosehosting, scaleway, vultr or generic"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "${tlsProvider}" != "fallback" && "${tlsProvider}" != "le-prod" && "${tlsProvider}" != "le-staging" ]]; then
|
|
echo "--tls-provider must be one of: le-prod, le-staging, fallback"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -z "${dnsProvider}" ]]; then
|
|
echo "--dns-provider is required (noop, manual)"
|
|
exit 1
|
|
elif [[ "${dnsProvider}" != "noop" && "${dnsProvider}" != "manual" ]]; then
|
|
echo "--dns-provider must be one of : manual, noop"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
echo "##############################################"
|
|
echo " Cloudron Setup (${requestedVersion}) "
|
|
echo "##############################################"
|
|
echo ""
|
|
echo " Follow setup logs in a second terminal with:"
|
|
echo " $ tail -f ${LOG_FILE}"
|
|
echo ""
|
|
echo " Join us at https://chat.cloudron.io for any questions."
|
|
echo ""
|
|
|
|
if [[ "${initBaseImage}" == "true" ]]; then
|
|
echo "=> Updating apt and installing script dependencies"
|
|
if ! apt-get update &>> "${LOG_FILE}"; then
|
|
echo "Could not update package repositories"
|
|
exit 1
|
|
fi
|
|
|
|
if ! apt-get install curl python3 ubuntu-standard -y &>> "${LOG_FILE}"; then
|
|
echo "Could not install setup dependencies (curl)"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
echo "=> Checking version"
|
|
if [[ "${sourceTarballUrl}" == "" ]]; then
|
|
releaseJson=$($curl -s "${versionsUrl}")
|
|
if [[ "$requestedVersion" == "latest" ]]; then
|
|
pre=$([[ "${prerelease}" == "true" ]] && echo "null" || echo "-pre")
|
|
version=$(echo "${releaseJson}" | python3 -c "import json,sys,collections;obj=json.load(sys.stdin, object_pairs_hook=collections.OrderedDict);latest=list(v for v in obj if '${pre}' not in v)[-1];print(latest)")
|
|
else
|
|
version="${requestedVersion}"
|
|
fi
|
|
if ! sourceTarballUrl=$(echo "${releaseJson}" | python3 -c 'import json,sys;obj=json.load(sys.stdin);print(obj[sys.argv[1]]["sourceTarballUrl"])' "${version}"); then
|
|
echo "No source code for version ${requestedVersion}"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Build data
|
|
if [[ -z "${dataJson}" ]]; then
|
|
if [[ -z "${restoreUrl}" ]]; then
|
|
data=$(cat <<EOF
|
|
{
|
|
"boxVersionsUrl": "${versionsUrl}",
|
|
"fqdn": "${domain}",
|
|
"provider": "${provider}",
|
|
"apiServerOrigin": "${apiServerOrigin}",
|
|
"tlsConfig": {
|
|
"provider": "${tlsProvider}"
|
|
},
|
|
"dnsConfig": {
|
|
"provider": "${dnsProvider}"
|
|
},
|
|
"backupConfig" : {
|
|
"provider": "filesystem",
|
|
"backupFolder": "/var/backups",
|
|
"key": "${encryptionKey}"
|
|
},
|
|
"updateConfig": {
|
|
"prerelease": ${prerelease}
|
|
},
|
|
"version": "${version}"
|
|
}
|
|
EOF
|
|
)
|
|
else
|
|
data=$(cat <<EOF
|
|
{
|
|
"boxVersionsUrl": "${versionsUrl}",
|
|
"fqdn": "${domain}",
|
|
"provider": "${provider}",
|
|
"apiServerOrigin": "${apiServerOrigin}",
|
|
"restore": {
|
|
"url": "${restoreUrl}",
|
|
"key": "${encryptionKey}"
|
|
},
|
|
"version": "${version}"
|
|
}
|
|
EOF
|
|
)
|
|
fi
|
|
else
|
|
data="${dataJson}"
|
|
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
|
|
|
|
echo "=> Installing version ${version} (this takes some time) ..."
|
|
echo "${data}" > "${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}"
|
|
|
|
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
|
|
[[ -z "$domain" ]] && break # with no domain, we are up and running
|
|
[[ "$status" == *"\"tls\": true"* ]] && break # with a domain, wait for the cert
|
|
fi
|
|
sleep 10
|
|
done
|
|
|
|
if [[ -n "${domain}" ]]; then
|
|
echo -e "\n\nVisit https://my.${domain} to finish setup once the server has rebooted.\n"
|
|
else
|
|
echo -e "\n\nVisit https://<IP> to finish setup once the server has rebooted.\n"
|
|
fi
|
|
|
|
if [[ "${rebootServer}" == "true" ]]; then
|
|
echo -e "\n\nRebooting this server now to let bootloader changes take effect.\n"
|
|
systemctl reboot
|
|
fi
|