Created
February 13, 2025 13:23
-
-
Save jacoblindqvist/e229dc9688e8a20aad1d3ce1d0e421e7 to your computer and use it in GitHub Desktop.
install.sh 8.0.0 for Enhance
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # The purpose of this script is to bootstrap the Enhance installation by downloading | |
| # and installing as systemd daemon the speficied version of controld. | |
| set -e | |
| export DEBIAN_FRONTEND="noninteractive" | |
| print_help() { | |
| echo "Usage: | |
| Interactive Mode: | |
| $0 | |
| Non Interactive Mode: | |
| Control panel installation: | |
| $0 --control-panel \\ | |
| App server installation: | |
| $0 --control-panel-api-url <control panel API URL> \\ | |
| --reg-key <registration key> \\ | |
| " | |
| } | |
| # default values | |
| restart_stopped_containers=true | |
| non_interactive=false | |
| for key in "$@"; do | |
| case "${key}" in | |
| --control-panel) | |
| control_panel=true | |
| ;; | |
| --control-panel-api-url) | |
| control_panel_api_url="$2" | |
| ;; | |
| --reg-key) | |
| reg_key="$2" | |
| ;; | |
| --registry) | |
| registry_hostname="$2" | |
| ;; | |
| --registry-user) | |
| registry_user="$2" | |
| ;; | |
| --registry-password) | |
| registry_password="$2" | |
| ;; | |
| --apt-repository) | |
| apt_repository="$2" | |
| ;; | |
| -h | --help) | |
| print_help | |
| exit 0 | |
| ;; | |
| esac | |
| shift | |
| done | |
| # Error codes | |
| invalid_arg=2 | |
| unsupported_distro=3 | |
| control_panel_unreachable=4 | |
| # Paths | |
| data_dir="/var/local/enhance" | |
| www_dir="/var/www" | |
| # Control Panel Paths | |
| # Note changing this paths will break other Enhance components (such as apachecd) | |
| # that may rely on these invariants. | |
| controld_daemon="enhcontrold" | |
| controld_work_dir="${data_dir}/controld" | |
| database_path="${controld_work_dir}/db.sqlite" | |
| config_path="${controld_work_dir}/conf.json" | |
| install_path="${controld_work_dir}/${controld_daemon}" | |
| control_panel_dir="${www_dir}/control-panel" | |
| orchd_static_dir="${control_panel_dir}/assets" | |
| screenshots_dir="${control_panel_dir}/screenshots" | |
| # TLS | |
| cert_dir="/etc/ssl/certs/enhance" | |
| key_dir="/etc/ssl/private/enhance" | |
| ca_trust_dir="/usr/share/ca-certificates" | |
| # Distros | |
| distro="unknown" | |
| distro_version="unknown" | |
| supported_distros=("Ubuntu" "Pop!_OS") | |
| minimum_os_version="20.04" | |
| # Docker tags | |
| orchd_tag="${orchd_tag:-latest-release}" | |
| # prompt config | |
| re_configure=false | |
| allow_unreachable_domain=false | |
| # system /etc/hosts file path | |
| etc_hosts="/etc/hosts" | |
| # domain and orchd api url validation regex | |
| domain_regex_base='(([a-zA-Z0-9]{1,63}(-*[a-zA-Z0-9]{1,63})*)\.)+[a-zA-Z]{2,63}' | |
| api_regex="^https?:\/\/${domain_regex_base}\/api$" | |
| domain_regex="^${domain_regex_base}$" | |
| # sets the control_panel variable according to the user provided input | |
| # i.e if control_panel api url is provided control_panel is set to false | |
| determine_control_panel() { | |
| if [[ "${control_panel}" = true ]] && [[ -n "${control_panel_api_url}" ]]; then | |
| echo "[$(date)] Error: Installation must be either for a control panel or an app server, not both" | |
| exit "${invalid_arg}" | |
| fi | |
| if [[ -n "${control_panel_api_url}" ]]; then | |
| control_panel=false | |
| elif [[ -n "${domain}" ]]; then | |
| control_panel=true | |
| fi | |
| } | |
| # helper function that displays the current value | |
| # and reminds user that pressing enter will keep the current | |
| display_current_value() { | |
| value="${1}" | |
| if [[ -n "${value}" ]]; then | |
| echo "Current Value: '${value}'" | |
| echo "Press Enter to use current" | |
| fi | |
| } | |
| # Retries the given command a pre-determined amount of times until it succeeds | |
| # or the number of attempts reaches the limit. | |
| # $1 - The command to run. | |
| # $2 - The maximum number of attempts (default: 10). | |
| # $3 - The interval of time in seconds between consecutive retries (default: 2). | |
| retry_cmd() { | |
| cmd="${1}" | |
| max_attempts="${2-10}" | |
| interval_s="${3-2}" | |
| n=0 | |
| until [[ "$n" -ge "${max_attempts}" ]]; do | |
| echo "[$(date)] Attempt ${n} to run: ${cmd}" | |
| ${cmd} && break | |
| n=$((n + 1)) | |
| sleep "${interval_s}" | |
| done | |
| [[ "$n" -lt "${max_attempts}" ]] | |
| } | |
| # Installs all the required dependencies of Enhance in Ubuntu. | |
| install_ubuntu_deps() { | |
| # pause between apt-get commands to avoid `Could not get lock /var/lib/apt/lists/lock` error | |
| readonly APT_CMD_PAUSE=3 | |
| sudo apt-get update -y | |
| sleep $APT_CMD_PAUSE | |
| sudo apt-get install -y \ | |
| apt-transport-https \ | |
| ca-certificates \ | |
| curl \ | |
| iproute2 \ | |
| jq \ | |
| libsqlite3-dev \ | |
| libssl-dev \ | |
| policycoreutils \ | |
| software-properties-common \ | |
| unzip \ | |
| uuid \ | |
| rsync \ | |
| gnupg \ | |
| dnsutils \ | |
| btrfs-progs | |
| # check if docker-ce is not installed | |
| if [[ "$(dpkg-query -W -f='${Status}' docker-ce 2>/dev/null | grep -c "ok installed")" -eq 0 ]]; then | |
| # Docker | |
| # add docker gpg keys | |
| curl -fsSL https://download.docker.com/linux/ubuntu/gpg | | |
| sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg | |
| echo \ | |
| "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ | |
| $(lsb_release -cs) stable" | | |
| sudo tee /etc/apt/sources.list.d/docker.list >/dev/null | |
| sudo apt-get update | |
| sleep $APT_CMD_PAUSE | |
| sudo apt-get install -y docker-ce | |
| fi | |
| sleep $APT_CMD_PAUSE | |
| sudo apt-get clean | |
| } | |
| # Installs all the dependencies required by Enhance to run, and created the required | |
| # filesystem structure. | |
| install_deps() { | |
| distro_name="${distro}-${distro_version}" | |
| echo -e "\n[$(date)] Installing dependencies for ${distro_name}" | |
| install_ubuntu_deps | |
| # create required dirs (owned by root) if they don't exist | |
| for dir in "${cert_dir}" "${key_dir}" "${ca_trust_dir}" "${controld_work_dir}" "${www_dir}"; do | |
| sudo mkdir -p "${dir}" | |
| done | |
| sudo chmod 600 "${controld_work_dir}" | |
| if [[ "${control_panel}" = true ]]; then | |
| sudo mkdir -p "${control_panel_dir}" | |
| fi | |
| # add root to docker group so controld can connect to the docker daemon | |
| # (root is not in the docker group by default) | |
| sudo usermod -a -G docker root | |
| sudo docker login "${registry_hostname}" --username "${registry_user}" --password-stdin <<<"${registry_password}" | |
| # start the docker daemon with every system boot | |
| sudo systemctl enable docker | |
| sudo systemctl start docker | |
| } | |
| # Extracts a file from a Docker image. | |
| # $1 - The Docker image. | |
| # $2 - The path of the file within the image. | |
| # $3 - The output path where to extract the file. | |
| extract_docker_file() { | |
| img="${1}" | |
| file_path="${2}" | |
| output_path="${3}" | |
| echo "[$(date)] Extracting ${file_path} from Docker image ${img}" | |
| retry_cmd "sudo docker pull ${img}" | |
| # create the container without starting it so that we can extract files from | |
| # it and save the container id | |
| id=$(sudo docker create "${img}") | |
| # extract the file | |
| sudo docker cp "${id}:${file_path}" "${output_path}" | |
| # remove the container after extraction is complete | |
| sudo docker rm "${id}" | |
| } | |
| # Downloads and installs the controld binary to bootstrap the installation. Other | |
| # services are installed by controld itself. | |
| # $1 - The controld tag. | |
| install_controld() { | |
| controld_tag="8.0.0" | |
| echo -e "\n[$(date)] Installing controld version: ${controld_tag}" | |
| # pull the release image | |
| controld_img="${registry_hostname}/controld:${controld_tag}" | |
| controld_bin="/usr/bin/enhance/controld" | |
| extract_docker_file "${controld_img}" "${controld_bin}" "${install_path}" | |
| # set controld binary owner and permissions | |
| sudo chmod +x "${install_path}" | |
| sudo chown root "${install_path}" | |
| sudo chgrp root "${install_path}" | |
| } | |
| # Stores the (JSON) configurations of controld which includes sensitive data, and | |
| # can be updated over time by controld itself. | |
| store_configuration() { | |
| configuration="{ | |
| \"registry\": { | |
| \"url\": \"${registry_hostname}\", | |
| \"user\": \"${registry_user}\", | |
| \"secret\": \"${registry_password}\" | |
| }, | |
| \"reg_key\": \"${reg_key}\" | |
| }" | |
| echo "${configuration}" | sudo tee "${config_path}" 1>/dev/null | |
| sudo chmod 600 "${config_path}" | |
| } | |
| # Here we register controld's systemd daemon and start it, the rest is taken care | |
| # of by controld. | |
| create_controld_systemd_unit() { | |
| echo -e "\n[$(date)] Creating ${controld_daemon} systemd service unit" | |
| docker_path="$(which docker)" | |
| openssl_path="$(which openssl)" | |
| ip_path="$(sudo which ip)" | |
| sh_path="$(which sh)" | |
| # set contrld's execution mode | |
| if [[ "${control_panel}" = true ]]; then | |
| mode=master | |
| key_pass="pass:${registry_password}" | |
| control_panel_domain="${domain}" | |
| else | |
| mode=slave | |
| key_pass="" | |
| control_panel_domain="${control_panel_api_url}" | |
| fi | |
| # NOTE: service file is defined inline so that it doesn't have to be | |
| # downloaded separately (plus it's easier to subsitute env var values | |
| # in-script). In the future we may bundle such dependencies in one place in | |
| # a zip file or similar. | |
| unit_conf=" | |
| [Unit] | |
| Description=Enhance control daemon service | |
| Wants=network-online.target | |
| After=network.target network-online.target | |
| Requires=network.target | |
| [Service] | |
| StartLimitInterval=5 | |
| User=root | |
| Group=root | |
| Type=simple | |
| Restart=always | |
| RestartSec=1 | |
| Environment=CA_TRUST_DIR=${ca_trust_dir} | |
| Environment=CERT_DIR=${cert_dir} | |
| Environment=CONTROL_PANEL_DIR=${control_panel_dir} | |
| Environment=CONTROL_PANEL_DOMAIN=${control_panel_domain} | |
| Environment=DATABASE_URL=${database_path} | |
| Environment=DATA_DIR=${data_dir} | |
| Environment=DOCKER_PATH=${docker_path} | |
| Environment=INSTALL_PATH=${install_path} | |
| Environment=IP_PATH=${ip_path} | |
| Environment=JWT_SECRET_DIR=${key_dir} | |
| Environment=KEY_DIR=${key_dir} | |
| Environment=KEY_PASS=${key_pass} | |
| Environment=MODE=${mode} | |
| Environment=OPENSSL_PATH=${openssl_path} | |
| Environment=ORCHD_TAG=${orchd_tag} | |
| Environment=RESTART_STOPPED_CONTAINERS=${restart_stopped_containers} | |
| Environment=RUST_LOG=${rust_log} | |
| Environment=SCREENSHOTS_DIR=${screenshots_dir} | |
| Environment=STATIC_DIR=${orchd_static_dir} | |
| Environment=WWW_DIR=${www_dir} | |
| WorkingDirectory=${controld_work_dir} | |
| ExecStart=${sh_path} -c \"${install_path}\" | |
| [Install] | |
| WantedBy=multi-user.target" | |
| unit_filename="${controld_daemon}.service" | |
| unit_conf_path="/etc/systemd/system/${unit_filename}" | |
| echo "Creating conf in ${unit_conf_path}" | |
| echo "${unit_conf}" | sudo tee "${unit_conf_path}" 1>/dev/null | |
| # enable (auto-start) controld service and start it | |
| echo "Enabling and starting ${unit_filename}" | |
| sudo systemctl enable "${unit_filename}" | |
| sudo systemctl start "${unit_filename}" | |
| } | |
| install_apt_repo() { | |
| sudo apt install -y gnupg coreutils | |
| if [ -z "${apt_repository}" ]; then | |
| apt_repository="https://apt.enhance.com" | |
| fi | |
| mkdir -p /usr/share/keyrings | |
| curl -o /tmp/enhance-pub-key.gpg $apt_repository/pgp-key.public | |
| cat /tmp/enhance-pub-key.gpg | gpg --dearmor >/tmp/enhance-pub-key-dearmored.gpg | |
| sudo cp /tmp/enhance-pub-key-dearmored.gpg /usr/share/keyrings/enhance.gpg | |
| echo "deb [arch=amd64, signed-by=/usr/share/keyrings/enhance.gpg] ${apt_repository} $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/enhance.list >/dev/null | |
| sudo apt update | |
| } | |
| install_appcd() { | |
| echo "Installing appcd and building chroot, this may take a long time..." | |
| if [ -z "${appcd_version}" ]; then | |
| sudo apt -y install appcd | |
| else | |
| sudo apt -y install "appcd=${appcd_version}" | |
| fi | |
| } | |
| install_control_panel_secondary() { | |
| echo "Fetching version from master server at ${control_panel_api_url}/version" | |
| # Fetch version from the API | |
| version=$(curl -k -s $control_panel_api_url/version) | |
| echo "Version of master server is ${version}" | |
| # Check if the version is in valid SemVer format | |
| if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "Invalid control panel version format: $version" | |
| exit 1 | |
| fi | |
| # Compare the version with 12.0.0 | |
| version_comparison=$(echo -e "$version\n11.99.99" | sort -V | head -n1) | |
| if [[ "$version_comparison" == "$version" ]]; then | |
| echo "Version is < 12.0.0" | |
| install_control_panel_secondary_legacy | |
| else | |
| echo "Version is >= 12.0.0" | |
| install_control_panel_secondary_current | |
| fi | |
| } | |
| install_control_panel_secondary_legacy() { | |
| echo "Installing legacy secondary server" | |
| install_deps | |
| install_appcd | |
| install_controld | |
| store_configuration | |
| create_controld_systemd_unit | |
| echo "Done, go to your control panel to manage the new server" | |
| } | |
| install_control_panel_secondary_current() { | |
| echo "Installing secondary server for >= 12.0.0 with reg key $reg_key" | |
| apt-get install -y ecp-core ecp-filerd | |
| control_panel_api_url="${control_panel_api_url#https://}" # Removes https:// from the start | |
| control_panel_api_url="${control_panel_api_url%/api}" # Removes /api from the end | |
| ecp join $control_panel_api_url $reg_key | |
| echo "Done, go to your control panel to manage the new server" | |
| } | |
| ## Execution starts here | |
| if [[ "${EUID}" != 0 ]]; then | |
| if sudo true; then | |
| echo "" | |
| else | |
| echo "[$(date)] Error: Please run this script with root privileges!" | |
| exit 1 | |
| fi | |
| fi | |
| if [[ -d "${data_dir}" ]]; then | |
| echo "[$(date)] Error: Cannot install enhance '${data_dir}' already exists!" | |
| exit 1 | |
| fi | |
| echo -e "[$(date)] Welcome to Enhance Installation \n\n" | |
| echo "Installing lsb-release if not present" | |
| apt-get install -y lsb-release | |
| determine_control_panel | |
| echo "Installing apt repo" | |
| install_apt_repo | |
| if [[ -z "$control_panel_api_url" ]]; then | |
| clear | |
| echo "Installing fresh control panel" | |
| apt-get -o DPkg::Lock::Timeout=120 -y install ecp-core | |
| apt-get -o DPkg::Lock::Timeout=120-y install ecp-filerd | |
| sleep 5 | |
| apt-get -o DPkg::Lock::Timeout=120 -y install orchd | |
| ecp init | |
| else | |
| echo "Installing secondary server" | |
| install_control_panel_secondary | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment