#!/bin/bash # Define PATH PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin # Export PATH export PATH # Command-line options # ============================================================================== # # --version # Use the --version option to install a special version for installation. default is latest, for example: # # $ sudo bash install.sh --version "0.8.25" # # --port <9000> # Use the --port option to set Websoft9 cosole port. default is 9000, for example: # # $ sudo bash install.sh --port 9001 # # --channel # Use the --channel option to install a release(production) or dev distribution. default is release, for example: # # $ sudo bash install.sh --channel release # # --path # Use the --path option to for installation path for example: # # $ sudo bash install.sh --path "/data/websoft9/source" # # --apps # Use the --apps option to set Websoft9 appstore dispaly. default is null and display all applicaionts. If you set it, appstore only display the defined, for example: # # $ sudo bash install.sh --apps "wordpress,gitlab" # # --mirrors # Use the --mirrors option to set docker image mirrors when can not pull image from docker-hub, for example: # # $ sudo bash install.sh --mirrors "https://docker.rainbond.cc,https://registry.inner.websoft9.cn" # # --devto # Use the --devto option to developer mode, devto is the developer code path, for example: # # $ sudo bash install.sh --devto "/data/dev/mycode" # # ============================================================================== # 设置参数的默认值 version="latest" channel="release" path="/data/websoft9/source" apps="" mirrors="https://docker.rainbond.cc,https://registry.inner.websoft9.cn" # 获取参数值 while [[ $# -gt 0 ]]; do case $1 in --version) shift if [[ $1 == --* ]]; then echo "Missing value for --version" exit 1 fi version="$1" shift ;; --port) shift if [[ $1 == --* ]]; then echo "Missing value for --port" exit 1 fi port="$1" shift ;; --channel) shift if [[ $1 == --* ]]; then echo "Missing value for --channel" exit 1 fi channel="$1" shift ;; --path) shift if [[ $1 == --* ]]; then echo "Missing value for --path" exit 1 fi path="$1" shift ;; --apps) shift if [[ $1 == --* ]]; then echo "Missing value for --apps" exit 1 fi apps="$1" shift ;; --mirrors) shift if [[ $1 == --* ]]; then echo "Missing value for --mirrors" exit 1 fi mirrors="$1" shift ;; --devto) shift if [[ $1 == --* ]]; then echo "Missing value for --devto" exit 1 fi devto="$1" shift ;; *) echo "Unknown parameter: $1" exit 1 ;; esac done if [ -n "$port" ]; then export port else export port=9000 fi starttime=$(date +%s) # Check is install or upgrade if systemctl cat websoft9 >/dev/null 2>&1 && systemctl cat cockpit >/dev/null 2>&1 && sudo docker ps -a --format '{{.Names}}' 2>/dev/null | grep -q '^websoft9-apphub'; then echo "execute_mode=upgrade" export execute_mode="upgrade" else echo "execute_mode=install" export execute_mode="install" fi # 输出参数值 echo -e "\n------ Welcome to install Websoft9, it will take 3-5 minutes ------" echo -e "\nYour installation parameters are as follows: " echo "--version: $version" echo "--port: $port" echo "--channel: $channel" echo "--path: $path" echo "--apps: $apps" echo "--mirrors: $mirrors" echo "--devto: $devto" echo -e "\nYour OS: " cat /etc/os-release | head -n 3 2>/dev/null # Define global vars # export var can send it to subprocess export http_port=80 export https_port=443 export install_path=$path export channel export version export apps export mirrors export systemd_path="/opt/websoft9/systemd" export source_zip="websoft9-$version.zip" export source_unzip="websoft9" export source_github_pages="https://websoft9.github.io/websoft9" # inotify-tools is at epel-release export repo_tools_yum="epel-release" export tools_yum="git curl wget yum-utils jq bc unzip inotify-tools" export tools_apt="git curl wget jq bc unzip inotify-tools" export docker_network="websoft9" export artifact_url="https://artifact.websoft9.com/$channel/websoft9" # export OS release environments if [ -f /etc/os-release ]; then . /etc/os-release else echo "Can't judge your Linux distribution" exit 1 fi echo Install from url: $artifact_url if [ -d "$install_path" ]; then echo "Directory $install_path already exists and installation will cover it." else sudo mkdir -p "$install_path" fi # Define common functions Wait_apt() { # Function to check if apt is locked local lock_files=("/var/lib/dpkg/lock" "/var/lib/apt/lists/lock") for lock_file in "${lock_files[@]}"; do while fuser "${lock_file}" >/dev/null 2>&1 ; do echo "${lock_file} is locked by another process. Waiting..." sleep 5 done done echo "APT locks are not held by any processes. You can proceed." } export -f Wait_apt install_tools(){ echo_prefix_tools=$'\n[Tools] - ' echo "$echo_prefix_tools Starting install necessary tool..." if [ "$ID" = "rhel" ] || [ "$ID" = "ol" ]; then RHEL_VERSION=${VERSION_ID%%.*} sudo yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-${RHEL_VERSION}.noarch.rpm >/dev/null if [ $? -ne 0 ]; then exit 1 fi elif [ "$ID" = "centos" ] || [ "$ID" = "rocky" ]; then sudo yum install -y "$repo_tools_yum" >/dev/null if [ $? -ne 0 ]; then exit 1 fi elif [ "$ID" = "amzn" ]; then sudo amazon-linux-extras install epel -y >/dev/null if [ $? -ne 0 ]; then exit 1 fi fi dnf --version >/dev/null 2>&1 dnf_status=$? yum --version >/dev/null 2>&1 yum_status=$? apt --version >/dev/null 2>&1 apt_status=$? if [ $dnf_status -eq 0 ]; then for package in $tools_yum; do echo "Start to install $package" sudo dnf install -y $package > /dev/null if [ $? -ne 0 ]; then exit 1 fi done elif [ $yum_status -eq 0 ]; then for package in $tools_yum; do echo "Start to install $package" sudo yum install -y $package > /dev/null if [ $? -ne 0 ]; then exit 1 fi done elif [ $apt_status -eq 0 ]; then while fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do echo "Waiting for other software managers to finish..." sleep 5 done sudo apt-get update -y 1>/dev/null 2>&1 for package in $tools_apt; do echo "Start to install $package" sudo apt-get install $package -y > /dev/null if [ $? -ne 0 ]; then exit 1 fi done else echo "You system can not install Websoft9 because not have available Linux Package Manager" exit 1 fi } download_source() { echo_prefix_source=$'\n[Download Source] - ' echo "$echo_prefix_source Download Websoft9 source code from $artifact_url/$source_zip" find . -type f -name "websoft9*.zip*" -exec rm -f {} \; wget "$artifact_url/$source_zip" if [ $? -ne 0 ]; then echo "Failed to download source package." exit 1 fi sudo unzip -o "$source_zip" -d "$install_path" > /dev/null if [ $? -ne 0 ]; then echo "Failed to unzip source package." exit 1 fi cp -r $install_path/$source_unzip/* "$install_path" if [ $? -ne 0 ]; then echo "Move directory failed" exit 1 fi rm -rf "$source_zip" "$install_path/$source_unzip" } check_ports() { local ports=("$@") echo "Stop Websoft9 Proxy and Cockpit service for reserve ports..." sudo docker stop websoft9-proxy 2>/dev/null || echo "docker stop websoft9-proxy not need " for port in "${ports[@]}"; do if [[ $port =~ ^[0-9]+$ ]] && [ $port -ge 0 ] && [ $port -le 65535 ]; then if ss -tuln | grep ":$port " >/dev/null && ! systemctl status cockpit.socket | grep "$port" >/dev/null; then echo "Port $port is in use or not in cockpit.socket, install failed" exit 1 fi else echo "Invalid port: $port" exit 1 fi done echo "All ports are available" } merge_json_files() { local target_path="/etc/docker/daemon.json" python3 - </dev/null import json import urllib.request import os def merge_json_files(file1, file2): print("Merge from local file... ") with open(file1, 'r') as f1, open(file2, 'r') as f2: data1 = json.load(f1) data2 = json.load(f2) merged_data = {**data1, **data2} with open(file1, 'w') as f: json.dump(merged_data, f, indent=4) def download_and_merge(url, file_path): print("Download daemon.json from url and merge... ") with urllib.request.urlopen(url) as response: data = json.loads(response.read().decode()) with open(file_path, 'r') as f: local_data = json.load(f) merged_data = {**local_data, **data} with open(file_path, 'w') as f: json.dump(merged_data, f, indent=4) # Create target file if it does not exist if not os.path.exists("${target_path}"): os.makedirs(os.path.dirname("${target_path}"), exist_ok=True) with open("${target_path}", 'w') as f: json.dump({}, f) if os.path.exists("${install_path}/docker/daemon.json"): merge_json_files("${target_path}", "${install_path}/docker/daemon.json") elif urllib.request.urlopen("${source_github_pages}/docker/daemon.json").getcode() == 200: download_and_merge("${source_github_pages}/docker/daemon.json", "${target_path}") else: print("No target daemon.json file need to merged") EOF if [ $? -ne 0 ]; then echo "merge daemon.json failed, but install continue running" fi } set_docker(){ echo "Set Docker for Websoft9 backend service..." merge_json_files if ! systemctl is-active --quiet firewalld; then echo "firewalld is not running" else echo "Set firewall for Docker..." sudo sudo firewall-cmd --permanent --new-zone=docker 2> /dev/null sudo firewall-cmd --permanent --zone=docker --add-interface=docker0 2> /dev/null sudo firewall-cmd --permanent --zone=docker --set-target=ACCEPT sudo firewall-cmd --reload sudo systemctl stop firewalld sudo systemctl disable firewalld fi if [ "$execute_mode" = "install" ]; then sudo systemctl restart docker fi } install_backends() { echo_prefix_backends=$'\n[Backend] - ' echo "$echo_prefix_backends Install backend docker services" set_docker cd "$install_path/docker" if [ $? -ne 0 ]; then echo "Failed to change directory." exit 1 fi sudo docker network inspect $docker_network >/dev/null 2>&1 if [ $? -eq 0 ]; then echo "Docker network '$docker_network' already exists." else sudo docker network create $docker_network if [ $? -ne 0 ]; then echo "Failed to create docker network." exit 1 fi fi # set to devloper mode if [ -n "$devto" ]; then sed -i "s|\(- \).*:/websoft9/apphub-dev|\1$devto:/websoft9/apphub-dev|g" docker-compose-dev.yml composefile=docker-compose-dev.yml else composefile=docker-compose.yml fi container_names=$(docker ps -a --format "{{.Names}}" --filter "name=websoft9") sudo docker compose -p websoft9 -f $composefile down # delete some dead containers that docker compose cannot deleted if [ ! -z "$container_names" ]; then echo "Deleting containers:" echo $container_names docker rm -f $container_names 2>/dev/null else echo "No containers to delete." fi DOCKER_CONFIG_FILE="/etc/docker/daemon.json" MIRROR_ADDRESS=$mirrors timeout 10s sudo docker compose -f $composefile pull if [ $? -eq 0 ]; then echo "Docker Compose pull succeeded" else echo "Can not pull images from docker hub, set mirrors..." if [ ! -f "$DOCKER_CONFIG_FILE" ]; then echo "{}" | sudo tee "$DOCKER_CONFIG_FILE" > /dev/null fi if command -v jq >/dev/null 2>&1; then MIRROR_ARRAY=$(echo $MIRROR_ADDRESS | sed 's/,/","/g' | sed 's/^/["/' | sed 's/$/"]/') #jq --arg mirrors "$MIRROR_ARRAY" '.["registry-mirrors"] = ($mirrors | fromjson)' "$DOCKER_CONFIG_FILE" > "$DOCKER_CONFIG_FILE.tmp" && sudo mv "$DOCKER_CONFIG_FILE.tmp" "$DOCKER_CONFIG_FILE" jq ".\"registry-mirrors\" = $MIRROR_ARRAY" "$DOCKER_CONFIG_FILE" > "$DOCKER_CONFIG_FILE.tmp" && sudo mv "$DOCKER_CONFIG_FILE.tmp" "$DOCKER_CONFIG_FILE" else echo "jq not installed!" exit 1 fi sudo systemctl daemon-reload sudo systemctl restart docker fi sudo docker compose -p websoft9 -f $composefile up -d --build if [ $? -ne 0 ]; then echo "Failed to start docker services." exit 1 fi if jq -e '.["registry-mirrors"]' "$DOCKER_CONFIG_FILE" > /dev/null; then jq 'del(.["registry-mirrors"])' "$DOCKER_CONFIG_FILE" > "${DOCKER_CONFIG_FILE}.tmp" && sudo mv "${DOCKER_CONFIG_FILE}.tmp" "$DOCKER_CONFIG_FILE" sudo systemctl daemon-reload sudo systemctl restart docker fi if [ "$execute_mode" = "install" ]; then sudo docker exec -i websoft9-apphub apphub setconfig --section domain --key wildcard_domain --value "" if [ -n "$apps" ]; then sudo docker exec -i websoft9-apphub apphub setconfig --section initial_apps --key keys --value "$apps" fi fi } install_systemd() { echo -e "\n\n-------- Systemd --------" echo_prefix_systemd=$'\n[Systemd] - ' echo "$echo_prefix_systemd Install Systemd service" if [ ! -d "$systemd_path" ]; then sudo mkdir -p "$systemd_path" fi sudo cp -r $install_path/systemd/script/* "$systemd_path" sudo cp -f "$install_path/systemd/websoft9.service" /lib/systemd/system/ if [ $? -ne 0 ]; then echo "Failed to copy Systemd service file." exit 1 fi sudo systemctl daemon-reload if [ $? -ne 0 ]; then echo "Failed to reload Systemd daemon." exit 1 fi sudo systemctl enable websoft9.service if [ $? -ne 0 ]; then echo "Failed to enable Systemd service." exit 1 fi sudo systemctl start websoft9 if [ $? -ne 0 ]; then echo "Failed to start Systemd service." exit 1 fi } #--------------- main----------------------------------------- log_path="$install_path/install.log" check_ports $http_port $https_port $port | tee -a $log_path install_tools | tee -a $log_path download_source | tee -a $log_path bash $install_path/install/install_docker.sh | tee -a $log_path if [ $? -ne 0 ]; then echo "install_docker failed with error $?. Exiting." exit 1 fi install_backends | tee -a $log_path install_systemd | tee -a $log_path bash $install_path/install/install_cockpit.sh | tee -a $log_path if [ $? -ne 0 ]; then echo "install_cockpit failed with error $?. Exiting." exit 1 fi bash $install_path/install/install_plugins.sh | tee -a $log_path if [ $? -ne 0 ]; then echo "install_plugins failed with error $?. Exiting." exit 1 fi echo "Restart Docker for Firewalld..." if [ "$execute_mode" = "install" ]; then sudo systemctl restart docker fi endtime=$(date +%s) runtime=$((endtime-starttime)) echo "Script execution time: $runtime seconds" echo -e "\n-- Install success! ------" echo "Access Websoft9 console by: http://Internet IP:$(grep ListenStream /lib/systemd/system/cockpit.socket | cut -d= -f2) and using Linux user for login"