diff --git a/.github/workflows/build-branch.yml b/.github/workflows/build-branch.yml index 7d32b73c4e1..8b7e1d5e291 100644 --- a/.github/workflows/build-branch.yml +++ b/.github/workflows/build-branch.yml @@ -316,6 +316,7 @@ jobs: retention-days: 2 path: | ${{ github.workspace }}/deploy/selfhost/setup.sh + ${{ github.workspace }}/deploy/selfhost/swarm.sh ${{ github.workspace }}/deploy/selfhost/restore.sh ${{ github.workspace }}/deploy/selfhost/docker-compose.yml ${{ github.workspace }}/deploy/selfhost/variables.env @@ -358,6 +359,7 @@ jobs: generate_release_notes: true files: | ${{ github.workspace }}/deploy/selfhost/setup.sh + ${{ github.workspace }}/deploy/selfhost/swarm.sh ${{ github.workspace }}/deploy/selfhost/restore.sh ${{ github.workspace }}/deploy/selfhost/docker-compose.yml ${{ github.workspace }}/deploy/selfhost/variables.env diff --git a/deploy/selfhost/README.md b/deploy/selfhost/README.md index ccd8bf328ce..e5a8089e646 100644 --- a/deploy/selfhost/README.md +++ b/deploy/selfhost/README.md @@ -55,18 +55,30 @@ Installing plane is a very easy and minimal step process. - User context used must have access to docker services. In most cases, use sudo su to switch as root user - Use the terminal (or gitbash) window to run all the future steps -### Downloading Latest Stable Release +### Downloading Latest Release ``` mkdir plane-selfhost cd plane-selfhost +``` + +#### For *Docker Compose* based setup +``` curl -fsSL -o setup.sh https://github.com/makeplane/plane/releases/latest/download/setup.sh chmod +x setup.sh ``` +#### For *Docker Swarm* based setup + +``` +curl -fsSL -o setup.sh https://github.com/makeplane/plane/releases/latest/download/swarm.sh + +chmod +x setup.sh +``` + --- ### Proceed with setup @@ -77,8 +89,9 @@ Lets get started by running the `./setup.sh` command. This will prompt you with the below options. +#### Docker Compose ```bash -Select a Action you want to perform: +Select an Action you want to perform: 1) Install (x86_64) 2) Start 3) Stop @@ -87,17 +100,42 @@ Select a Action you want to perform: 6) View Logs 7) Backup Data 8) Exit + Action [2]: 1 ``` For the 1st time setup, type "1" as action input. -This will create a create a folder `plane-app` or `plane-app-preview` (in case of preview deployment) and will download 2 files inside that +This will create a folder `plane-app` and will download 2 files inside that + +- `docker-compose.yaml` +- `plane.env` + +Again the `options [1-8]` will be popped up, and this time hit `8` to exit. + +#### Docker Swarm + +```bash +Select an Action you want to perform: + 1) Deploy Stack + 2) Remove Stack + 3) View Stack Status + 4) Redeploy Stack + 5) Upgrade + 6) View Logs + 7) Exit + +Action [3]: 1 +``` + +For the 1st time setup, type "1" as action input. + +This will create a create a folder `plane-app` and will download 2 files inside that - `docker-compose.yaml` - `plane.env` -Again the `options [1-8]` will be popped up and this time hit `8` to exit. +Again the `options [1-7]` will be popped up, and this time hit `7` to exit. --- @@ -116,7 +154,7 @@ There are many other settings you can play with, but we suggest you configure `E --- -### Continue with setup - Start Server +### Continue with setup - Start Server (Docker Compose) Lets again run the `./setup.sh` command. You will again be prompted with the below options. This time select `2` to start the sevices @@ -147,9 +185,11 @@ You have successfully self hosted `Plane` instance. Access the application by go --- -### Stopping the Server +### Stopping the Server / Remove Stack + +In case you want to make changes to `plane.env` variables, we suggest you to stop the services before doing that. -In case you want to make changes to `.env` variables, we suggest you to stop the services before doing that. +#### Docker Compose Lets again run the `./setup.sh` command. You will again be prompted with the below options. This time select `3` to stop the sevices @@ -171,14 +211,34 @@ If all goes well, you must see something like this ![Stop Services](images/stopped.png) +#### Docker Swarm + +Lets again run the `./setup.sh` command. You will again be prompted with the below options. This time select `2` to stop the sevices + +```bash +Select an Action you want to perform: + 1) Deploy Stack + 2) Remove Stack + 3) View Stack Status + 4) Redeploy Stack + 5) Upgrade + 6) View Logs + 7) Exit + +Action [3]: 2 +``` + +If all goes well, you will see the confirmation from docker cli + --- -### Restarting the Server +### Restarting the Server / Redeploy Stack -In case you want to make changes to `.env` variables, without stopping the server or you noticed some abnormalies in services, you can restart the services with RESTART option. +In case you want to make changes to `plane.env` variables, without stopping the server or you noticed some abnormalies in services, you can restart the services with `RESTART` / `REDEPLOY` option. Lets again run the `./setup.sh` command. You will again be prompted with the below options. This time select `4` to restart the sevices +#### Docker Compose ```bash Select a Action you want to perform: 1) Install (x86_64) @@ -197,14 +257,32 @@ If all goes well, you must see something like this ![Restart Services](images/restart.png) +#### Docker Swarm + +```bash + 1) Deploy Stack + 2) Remove Stack + 3) View Stack Status + 4) Redeploy Stack + 5) Upgrade + 6) View Logs + 7) Exit + +Action [3]: 4 +``` + +If all goes well, you will see the confirmation from docker cli + --- -### Upgrading Plane Version +### Upgrading Plane Version It is always advised to keep Plane up to date with the latest release. Lets again run the `./setup.sh` command. You will again be prompted with the below options. This time select `5` to upgrade the release. +#### Docker Compose + ```bash Select a Action you want to perform: 1) Install (x86_64) @@ -231,13 +309,41 @@ Once done, choose `8` to exit from prompt. Once done with making changes in `plane.env` file, jump on to `Start Server` +#### Docker Swarm + +Lets again run the `./setup.sh` command. You will again be prompted with the below options. This time select `5` to upgrade the release. + +```bash + 1) Deploy Stack + 2) Remove Stack + 3) View Stack Status + 4) Redeploy Stack + 5) Upgrade + 6) View Logs + 7) Exit + +Action [3]: 5 +``` + +By choosing this, it will stop the services and then will download the latest `docker-compose.yaml` and `plane.env`. + +Once done, choose `7` to exit from prompt. + +> It is very important for you to validate the `plane.env` for the new changes. + +Once done with making changes in `plane.env` file, jump on to `Redeploy Stack` + --- ### View Logs There would a time when you might want to check what is happening inside the API, Worker or any other container. -Lets again run the `./setup.sh` command. You will again be prompted with the below options. This time select `6` to view logs. +Lets again run the `./setup.sh` command. You will again be prompted with the below options. + +This time select `6` to view logs. + +#### Docker Compose ```bash Select a Action you want to perform: @@ -253,7 +359,22 @@ Select a Action you want to perform: Action [2]: 6 ``` +#### Docker Swarm + + +```bash + 1) Deploy Stack + 2) Remove Stack + 3) View Stack Status + 4) Redeploy Stack + 5) Upgrade + 6) View Logs + 7) Exit + +Action [3]: 6 +``` +#### Service Menu Options for Logs This will further open sub-menu with list of services ```bash Select a Service you want to view the logs for: @@ -267,9 +388,10 @@ Select a Service you want to view the logs for: 8) Redis 9) Postgres 10) Minio + 11) RabbitMQ 0) Back to Main Menu -Service: +Service: 3 ``` Select any of the service to view the logs e.g. `3`. Expect something similar to this @@ -323,7 +445,7 @@ Similarly, you can view the logs of other services. --- -### Backup Data +### Backup Data (Docker Compose) There would a time when you might want to backup your data from docker volumes to external storage like S3 or drives. @@ -355,7 +477,7 @@ Backup completed successfully. Backup files are stored in /....../plane-app/back --- -### Restore Data +### Restore Data (Docker Compose) When you want to restore the previously backed-up data, follow the instructions below. diff --git a/deploy/selfhost/docker-compose.yml b/deploy/selfhost/docker-compose.yml index 13cfafa2237..1c540c7f57f 100644 --- a/deploy/selfhost/docker-compose.yml +++ b/deploy/selfhost/docker-compose.yml @@ -55,28 +55,35 @@ x-app-env: &app-env APP_BASE_URL: ${APP_BASE_URL} AMQP_URL: ${AMQP_URL:-amqp://plane:plane@plane-mq:5672/plane} + #Replicas Environment Variables + WEB_REPLICAS: ${WEB_REPLICAS:-1} + SPACE_REPLICAS: ${SPACE_REPLICAS:-1} + ADMIN_REPLICAS: ${ADMIN_REPLICAS:-1} + API_REPLICAS: ${API_REPLICAS:-1} + WORKER_REPLICAS: ${WORKER_REPLICAS:-1} + BEAT_WORKER_REPLICAS: ${BEAT_WORKER_REPLICAS:-1} + LIVE_REPLICAS: ${LIVE_REPLICAS:-1} + services: web: image: ${DOCKERHUB_USER:-makeplane}/plane-frontend:${APP_RELEASE:-stable} - platform: ${DOCKER_PLATFORM:-} - pull_policy: if_not_present - restart: unless-stopped command: node web/server.js web deploy: replicas: ${WEB_REPLICAS:-1} + restart_policy: + condition: on-failure depends_on: - api - worker space: image: ${DOCKERHUB_USER:-makeplane}/plane-space:${APP_RELEASE:-stable} - platform: ${DOCKER_PLATFORM:-} - pull_policy: if_not_present - restart: unless-stopped command: node space/server.js space deploy: replicas: ${SPACE_REPLICAS:-1} + restart_policy: + condition: on-failure depends_on: - api - worker @@ -84,38 +91,35 @@ services: admin: image: ${DOCKERHUB_USER:-makeplane}/plane-admin:${APP_RELEASE:-stable} - platform: ${DOCKER_PLATFORM:-} - pull_policy: if_not_present - restart: unless-stopped command: node admin/server.js admin deploy: replicas: ${ADMIN_REPLICAS:-1} + restart_policy: + condition: on-failure depends_on: - api - web live: image: ${DOCKERHUB_USER:-makeplane}/plane-live:${APP_RELEASE:-stable} - platform: ${DOCKER_PLATFORM:-} - pull_policy: if_not_present - restart: unless-stopped command: node live/dist/server.js live environment: <<: [ *live-env ] deploy: replicas: ${LIVE_REPLICAS:-1} + restart_policy: + condition: on-failure depends_on: - api - web api: image: ${DOCKERHUB_USER:-makeplane}/plane-backend:${APP_RELEASE:-stable} - platform: ${DOCKER_PLATFORM:-} - pull_policy: if_not_present - restart: unless-stopped command: ./bin/docker-entrypoint-api.sh deploy: replicas: ${API_REPLICAS:-1} + restart_policy: + condition: on-failure volumes: - logs_api:/code/plane/logs environment: @@ -127,10 +131,11 @@ services: worker: image: ${DOCKERHUB_USER:-makeplane}/plane-backend:${APP_RELEASE:-stable} - platform: ${DOCKER_PLATFORM:-} - pull_policy: if_not_present - restart: unless-stopped command: ./bin/docker-entrypoint-worker.sh + deploy: + replicas: ${WORKER_REPLICAS:-1} + restart_policy: + condition: on-failure volumes: - logs_worker:/code/plane/logs environment: @@ -143,10 +148,11 @@ services: beat-worker: image: ${DOCKERHUB_USER:-makeplane}/plane-backend:${APP_RELEASE:-stable} - platform: ${DOCKER_PLATFORM:-} - pull_policy: if_not_present - restart: unless-stopped command: ./bin/docker-entrypoint-beat.sh + deploy: + replicas: ${BEAT_WORKER_REPLICAS:-1} + restart_policy: + condition: on-failure volumes: - logs_beat-worker:/code/plane/logs environment: @@ -159,10 +165,11 @@ services: migrator: image: ${DOCKERHUB_USER:-makeplane}/plane-backend:${APP_RELEASE:-stable} - platform: ${DOCKER_PLATFORM:-} - pull_policy: if_not_present - restart: "no" command: ./bin/docker-entrypoint-migrator.sh + deploy: + replicas: 1 + restart_policy: + condition: on-failure volumes: - logs_migrator:/code/plane/logs environment: @@ -173,9 +180,11 @@ services: plane-db: image: postgres:15.7-alpine - pull_policy: if_not_present - restart: unless-stopped command: postgres -c 'max_connections=1000' + deploy: + replicas: 1 + restart_policy: + condition: on-failure environment: <<: *db-env volumes: @@ -183,14 +192,19 @@ services: plane-redis: image: valkey/valkey:7.2.5-alpine - pull_policy: if_not_present - restart: unless-stopped + deploy: + replicas: 1 + restart_policy: + condition: on-failure volumes: - redisdata:/data plane-mq: image: rabbitmq:3.13.6-management-alpine - restart: always + deploy: + replicas: 1 + restart_policy: + condition: on-failure environment: <<: *mq-env volumes: @@ -198,9 +212,11 @@ services: plane-minio: image: minio/minio:latest - pull_policy: if_not_present - restart: unless-stopped command: server /export --console-address ":9090" + deploy: + replicas: 1 + restart_policy: + condition: on-failure environment: <<: *minio-env volumes: @@ -209,13 +225,17 @@ services: # Comment this if you already have a reverse proxy running proxy: image: ${DOCKERHUB_USER:-makeplane}/plane-proxy:${APP_RELEASE:-stable} - platform: ${DOCKER_PLATFORM:-} - pull_policy: if_not_present - restart: unless-stopped ports: - - ${NGINX_PORT}:80 + - target: 80 + published: ${NGINX_PORT:-80} + protocol: tcp + mode: host environment: <<: *proxy-env + deploy: + replicas: 1 + restart_policy: + condition: on-failure depends_on: - web - api @@ -224,7 +244,6 @@ services: volumes: pgdata: redisdata: - uploads: logs_api: logs_worker: diff --git a/deploy/selfhost/install.sh b/deploy/selfhost/install.sh index 1c2208cab7c..1ee0d6f352f 100755 --- a/deploy/selfhost/install.sh +++ b/deploy/selfhost/install.sh @@ -457,12 +457,13 @@ function viewLogs(){ echo " 8) Redis" echo " 9) Postgres" echo " 10) Minio" + echo " 11) RabbitMQ" echo " 0) Back to Main Menu" echo read -p "Service: " DOCKER_SERVICE_NAME - until (( DOCKER_SERVICE_NAME >= 0 && DOCKER_SERVICE_NAME <= 10 )); do - echo "Invalid selection. Please enter a number between 1 and 11." + until (( DOCKER_SERVICE_NAME >= 0 && DOCKER_SERVICE_NAME <= 11 )); do + echo "Invalid selection. Please enter a number between 0 and 11." read -p "Service: " DOCKER_SERVICE_NAME done @@ -481,6 +482,7 @@ function viewLogs(){ 8) viewSpecificLogs "plane-redis";; 9) viewSpecificLogs "plane-db";; 10) viewSpecificLogs "plane-minio";; + 11) viewSpecificLogs "plane-mq";; 0) askForAction;; *) echo "INVALID SERVICE NAME SUPPLIED";; esac @@ -499,6 +501,7 @@ function viewLogs(){ redis) viewSpecificLogs "plane-redis";; postgres) viewSpecificLogs "plane-db";; minio) viewSpecificLogs "plane-minio";; + rabbitmq) viewSpecificLogs "plane-mq";; *) echo "INVALID SERVICE NAME SUPPLIED";; esac else diff --git a/deploy/selfhost/swarm.sh b/deploy/selfhost/swarm.sh new file mode 100755 index 00000000000..49fe775766b --- /dev/null +++ b/deploy/selfhost/swarm.sh @@ -0,0 +1,612 @@ +#!/bin/bash + +BRANCH=${BRANCH:-master} +SERVICE_FOLDER=plane-app +SCRIPT_DIR=$PWD +PLANE_INSTALL_DIR=$PWD/$SERVICE_FOLDER +export APP_RELEASE="stable" +export DOCKERHUB_USER=makeplane + +export GH_REPO=makeplane/plane +export RELEASE_DOWNLOAD_URL="https://github.com/$GH_REPO/releases/download" +export FALLBACK_DOWNLOAD_URL="https://raw.githubusercontent.com/$GH_REPO/$BRANCH/deploy/selfhost" + +OS_NAME=$(uname) + +# Create necessary directories +mkdir -p $PLANE_INSTALL_DIR/archive + +DOCKER_FILE_PATH=$PLANE_INSTALL_DIR/docker-compose.yml +DOCKER_ENV_PATH=$PLANE_INSTALL_DIR/plane.env + +function print_header() { +clear + +cat <<"EOF" +-------------------------------------------- + ____ _ ///////// +| _ \| | __ _ _ __ ___ ///////// +| |_) | |/ _` | '_ \ / _ \ ///// ///// +| __/| | (_| | | | | __/ ///// ///// +|_| |_|\__,_|_| |_|\___| //// + //// +-------------------------------------------- +Project management tool from the future +-------------------------------------------- +EOF +} + +function checkLatestRelease(){ + echo "Checking for the latest release..." >&2 + local latest_release=$(curl -s https://api.github.com/repos/$GH_REPO/releases/latest | grep -o '"tag_name": "[^"]*"' | sed 's/"tag_name": "//;s/"//g') + if [ -z "$latest_release" ]; then + echo "Failed to check for the latest release. Exiting..." >&2 + exit 1 + fi + + echo $latest_release +} + +# Function to read stack name from env file +function readStackName() { + if [ -f "$DOCKER_ENV_PATH" ]; then + local saved_stack_name=$(grep "^STACK_NAME=" "$DOCKER_ENV_PATH" | cut -d'=' -f2) + if [ -n "$saved_stack_name" ]; then + stack_name=$saved_stack_name + return 1 + fi + fi + return 0 +} + +# Function to get stack name (either from env or user input) +function getStackName() { + read -p "Enter stack name [plane]: " input_stack_name + if [ -z "$input_stack_name" ]; then + input_stack_name="plane" + fi + stack_name=$input_stack_name + updateEnvFile "STACK_NAME" "$stack_name" "$DOCKER_ENV_PATH" + echo "Using stack name: $stack_name" +} + +function syncEnvFile(){ + echo "Syncing environment variables..." >&2 + if [ -f "$PLANE_INSTALL_DIR/plane.env.bak" ]; then + # READ keys of plane.env and update the values from plane.env.bak + while IFS= read -r line + do + # ignore if the line is empty or starts with # + if [ -z "$line" ] || [[ $line == \#* ]]; then + continue + fi + key=$(echo "$line" | cut -d'=' -f1) + value=$(getEnvValue "$key" "$PLANE_INSTALL_DIR/plane.env.bak") + if [ -n "$value" ]; then + updateEnvFile "$key" "$value" "$DOCKER_ENV_PATH" + fi + done < "$DOCKER_ENV_PATH" + + value=$(getEnvValue "STACK_NAME" "$PLANE_INSTALL_DIR/plane.env.bak") + if [ -n "$value" ]; then + updateEnvFile "STACK_NAME" "$value" "$DOCKER_ENV_PATH" + fi + fi + echo "Environment variables synced successfully" >&2 + rm -f $PLANE_INSTALL_DIR/plane.env.bak +} + +function getEnvValue() { + local key=$1 + local file=$2 + + if [ -z "$key" ] || [ -z "$file" ]; then + echo "Invalid arguments supplied" + exit 1 + fi + + if [ -f "$file" ]; then + grep -q "^$key=" "$file" + if [ $? -eq 0 ]; then + local value + value=$(grep "^$key=" "$file" | cut -d'=' -f2) + echo "$value" + else + echo "" + fi + fi +} + +function updateEnvFile() { + local key=$1 + local value=$2 + local file=$3 + + if [ -z "$key" ] || [ -z "$value" ] || [ -z "$file" ]; then + echo "Invalid arguments supplied" + exit 1 + fi + + if [ -f "$file" ]; then + # check if key exists in the file + grep -q "^$key=" "$file" + if [ $? -ne 0 ]; then + echo "$key=$value" >> "$file" + return + else + if [ "$OS_NAME" == "Darwin" ]; then + value=$(echo "$value" | sed 's/|/\\|/g') + sed -i '' "s|^$key=.*|$key=$value|g" "$file" + else + sed -i "s/^$key=.*/$key=$value/g" "$file" + fi + fi + else + echo "File not found: $file" + exit 1 + fi +} + +function download() { + cd $SCRIPT_DIR || exit 1 + TS=$(date +%s) + if [ -f "$PLANE_INSTALL_DIR/docker-compose.yml" ] + then + mv $PLANE_INSTALL_DIR/docker-compose.yml $PLANE_INSTALL_DIR/archive/$TS.docker-compose.yml + fi + + echo $RELEASE_DOWNLOAD_URL + echo $FALLBACK_DOWNLOAD_URL + echo $APP_RELEASE + + RESPONSE=$(curl -H 'Cache-Control: no-cache, no-store' -s -w "HTTPSTATUS:%{http_code}" "$RELEASE_DOWNLOAD_URL/$APP_RELEASE/docker-compose.yml?$(date +%s)") + BODY=$(echo "$RESPONSE" | sed -e 's/HTTPSTATUS\:.*//g') + STATUS=$(echo "$RESPONSE" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') + + if [ "$STATUS" -eq 200 ]; then + echo "$BODY" > $PLANE_INSTALL_DIR/docker-compose.yml + else + # Fallback to download from the raw github url + RESPONSE=$(curl -H 'Cache-Control: no-cache, no-store' -s -w "HTTPSTATUS:%{http_code}" "$FALLBACK_DOWNLOAD_URL/docker-compose.yml?$(date +%s)") + BODY=$(echo "$RESPONSE" | sed -e 's/HTTPSTATUS\:.*//g') + STATUS=$(echo "$RESPONSE" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') + + if [ "$STATUS" -eq 200 ]; then + echo "$BODY" > $PLANE_INSTALL_DIR/docker-compose.yml + else + echo "Failed to download docker-compose.yml. HTTP Status: $STATUS" + echo "URL: $RELEASE_DOWNLOAD_URL/$APP_RELEASE/docker-compose.yml" + mv $PLANE_INSTALL_DIR/archive/$TS.docker-compose.yml $PLANE_INSTALL_DIR/docker-compose.yml + exit 1 + fi + fi + + RESPONSE=$(curl -H 'Cache-Control: no-cache, no-store' -s -w "HTTPSTATUS:%{http_code}" "$RELEASE_DOWNLOAD_URL/$APP_RELEASE/variables.env?$(date +%s)") + BODY=$(echo "$RESPONSE" | sed -e 's/HTTPSTATUS\:.*//g') + STATUS=$(echo "$RESPONSE" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') + + if [ "$STATUS" -eq 200 ]; then + echo "$BODY" > $PLANE_INSTALL_DIR/variables-upgrade.env + else + # Fallback to download from the raw github url + RESPONSE=$(curl -H 'Cache-Control: no-cache, no-store' -s -w "HTTPSTATUS:%{http_code}" "$FALLBACK_DOWNLOAD_URL/variables.env?$(date +%s)") + BODY=$(echo "$RESPONSE" | sed -e 's/HTTPSTATUS\:.*//g') + STATUS=$(echo "$RESPONSE" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') + + if [ "$STATUS" -eq 200 ]; then + echo "$BODY" > $PLANE_INSTALL_DIR/variables-upgrade.env + else + echo "Failed to download variables.env. HTTP Status: $STATUS" + echo "URL: $RELEASE_DOWNLOAD_URL/$APP_RELEASE/variables.env" + mv $PLANE_INSTALL_DIR/archive/$TS.docker-compose.yml $PLANE_INSTALL_DIR/docker-compose.yml + exit 1 + fi + fi + + if [ -f "$DOCKER_ENV_PATH" ]; + then + cp "$DOCKER_ENV_PATH" "$PLANE_INSTALL_DIR/archive/$TS.env" + cp "$DOCKER_ENV_PATH" "$PLANE_INSTALL_DIR/plane.env.bak" + fi + + mv $PLANE_INSTALL_DIR/variables-upgrade.env $DOCKER_ENV_PATH + + syncEnvFile + + updateEnvFile "APP_RELEASE" "$APP_RELEASE" "$DOCKER_ENV_PATH" + +} +function deployStack() { + # Check if docker compose file and env file exist + if [ ! -f "$DOCKER_FILE_PATH" ] || [ ! -f "$DOCKER_ENV_PATH" ]; then + echo "Configuration files not found" + echo "Downloading it now......" + APP_RELEASE=$(checkLatestRelease) + download + fi + if [ -z "$stack_name" ]; then + getStackName + fi + echo "Starting ${stack_name} stack..." + + # Pull envs + if [ -f "$DOCKER_ENV_PATH" ]; then + set -o allexport; source $DOCKER_ENV_PATH; set +o allexport; + else + echo "Environment file not found: $DOCKER_ENV_PATH" + exit 1 + fi + + # Deploy the stack + docker stack deploy -c $DOCKER_FILE_PATH $stack_name + + echo "Waiting for services to be deployed..." + sleep 10 + + # Check migrator service + local migrator_service=$(docker service ls --filter name=${stack_name}_migrator -q) + if [ -n "$migrator_service" ]; then + echo ">> Waiting for Data Migration to finish" + while docker service ls --filter name=${stack_name}_migrator | grep -q "running"; do + echo -n "." + sleep 1 + done + echo "" + + # Get the most recent container for the migrator service + local migrator_container=$(docker ps -a --filter name=${stack_name}_migrator --latest -q) + + if [ -n "$migrator_container" ]; then + # Get the exit code of the container + local exit_code=$(docker inspect --format='{{.State.ExitCode}}' $migrator_container) + + if [ "$exit_code" != "0" ]; then + echo "Server failed to start ❌" + echo "Migration failed with exit code: $exit_code" + echo "Please check the logs for the 'migrator' service and resolve the issue(s)." + echo "Stop the services by running the command: ./swarm.sh stop" + exit 1 + else + echo " Data Migration completed successfully ✅" + fi + else + echo "Warning: Could not find migrator container to check exit status" + fi + fi + + # Check API service + local api_service=$(docker service ls --filter name=${stack_name}_api -q) + while docker service ls --filter name=${stack_name}_api | grep -q "running"; do + local running_container=$(docker ps --filter "name=${stack_name}_api" --filter "status=running" -q) + if [ -n "$running_container" ]; then + if docker container logs $running_container 2>/dev/null | grep -q "Application Startup Complete"; then + break + fi + fi + sleep 2 + done + + if [ -z "$api_service" ]; then + echo "Plane Server failed to start ❌" + echo "Please check the logs for the 'api' service and resolve the issue(s)." + echo "Stop the services by running the command: ./swarm.sh stop" + exit 1 + fi + echo " Plane Server started successfully ✅" + echo "" + echo " You can access the application at $WEB_URL" + echo "" +} + +function removeStack() { + if [ -z "$stack_name" ]; then + echo "Stack name not found" + exit 1 + fi + echo "Removing ${stack_name} stack..." + docker stack rm "$stack_name" + echo "Waiting for services to be removed..." + while docker stack ls | grep -q "$stack_name"; do + sleep 1 + done + sleep 20 + echo "Services stopped successfully ✅" +} + +function viewStatus() { + echo "Checking status of ${stack_name} stack..." + if [ -z "$stack_name" ]; then + echo "Stack name not found" + exit 1 + fi + docker stack ps "$stack_name" +} + +function redeployStack() { + removeStack + echo "ReDeploying ${stack_name} stack..." + deployStack +} + +function upgrade() { + + echo "Checking status of ${stack_name} stack..." + if [ -z "$stack_name" ]; then + echo "Stack name not found" + exit 1 + fi + + local latest_release=$(checkLatestRelease) + + echo "" + echo "Current release: $APP_RELEASE" + + if [ "$latest_release" == "$APP_RELEASE" ]; then + echo "" + echo "You are already using the latest release" + exit 0 + fi + + echo "Latest release: $latest_release" + echo "" + + # Check for confirmation to upgrade + echo "Do you want to upgrade to the latest release ($latest_release)?" + read -p "Continue? [y/N]: " confirm + + if [[ ! "$confirm" =~ ^[Yy]$ ]]; then + echo "Exiting..." + exit 0 + fi + + export APP_RELEASE=$latest_release + + # check if stack exists + echo "Upgrading ${stack_name} stack..." + + # check env file and take backup + if [ -f "$DOCKER_ENV_PATH" ]; then + cp "$DOCKER_ENV_PATH" "${DOCKER_ENV_PATH}.bak" + fi + + download + redeployStack +} + +function viewSpecificLogs() { + local service=$1 + + # Input validation + if [ -z "$service" ]; then + echo "Error: Please specify a service name" + return 1 + fi + + # Main loop for service logs + while true; do + # Get all running containers for the service + local running_containers=$(docker ps --filter "name=${stack_name}_${service}" --filter "status=running" -q) + + # If no running containers found, try service logs + if [ -z "$running_containers" ]; then + echo "No running containers found for ${stack_name}_${service}, checking service logs..." + if docker service inspect ${stack_name}_${service} >/dev/null 2>&1; then + echo "Press Ctrl+C or 'q' to exit logs" + docker service logs ${stack_name}_${service} -f + break + else + echo "Error: No running containers or services found for ${stack_name}_${service}" + return 1 + fi + return + fi + + # If multiple containers are running, let user choose + if [ $(echo "$running_containers" | grep -v '^$' | wc -l) -gt 1 ]; then + clear + echo "Multiple containers found for ${stack_name}_${service}:" + local i=1 + # Use regular arrays instead of associative arrays + container_ids=() + container_names=() + + while read -r container_id; do + if [ -n "$container_id" ]; then + local container_name=$(docker inspect --format '{{.Name}}' "$container_id" | sed 's/\///') + container_ids[$i]=$container_id + container_names[$i]=$container_name + echo "[$i] ${container_names[$i]} (${container_ids[$i]})" + i=$((i+1)) + fi + done <<< "$running_containers" + + echo -e "\nPlease select a container number:" + read -r selection + + if [[ "$selection" =~ ^[0-9]+$ ]] && [ -n "${container_ids[$selection]}" ]; then + local selected_container=${container_ids[$selection]} + clear + echo "Showing logs for container: ${container_names[$selection]}" + echo "Press Ctrl+C or 'q' to return to container selection" + + # Start watching logs in the background + docker container logs -f "$selected_container" & + local log_pid=$! + + while true; do + read -r -n 1 input + if [[ $input == "q" ]]; then + kill $log_pid 2>/dev/null + wait $log_pid 2>/dev/null + break + fi + done + clear + else + echo "Error: Invalid selection" + sleep 2 + fi + else + # Single container case + local container_name=$(docker inspect --format '{{.Name}}' "$running_containers" | sed 's/\///') + echo "Showing logs for container: $container_name" + echo "Press Ctrl+C or 'q' to exit logs" + docker container logs -f "$running_containers" & + local log_pid=$! + + while true; do + read -r -n 1 input + if [[ $input == "q" ]]; then + kill $log_pid 2>/dev/null + wait $log_pid 2>/dev/null + break + fi + done + break + fi + done +} + +function viewLogs(){ + + ARG_SERVICE_NAME=$2 + if [ -z "$ARG_SERVICE_NAME" ]; + then + echo + echo "Select a Service you want to view the logs for:" + echo " 1) Web" + echo " 2) Space" + echo " 3) API" + echo " 4) Worker" + echo " 5) Beat-Worker" + echo " 6) Migrator" + echo " 7) Proxy" + echo " 8) Redis" + echo " 9) Postgres" + echo " 10) Minio" + echo " 11) RabbitMQ" + echo " 0) Back to Main Menu" + echo + read -p "Service: " DOCKER_SERVICE_NAME + + until (( DOCKER_SERVICE_NAME >= 0 && DOCKER_SERVICE_NAME <= 11 )); do + echo "Invalid selection. Please enter a number between 0 and 11." + read -p "Service: " DOCKER_SERVICE_NAME + done + + if [ -z "$DOCKER_SERVICE_NAME" ]; + then + echo "INVALID SERVICE NAME SUPPLIED" + else + case $DOCKER_SERVICE_NAME in + 1) viewSpecificLogs "web";; + 2) viewSpecificLogs "space";; + 3) viewSpecificLogs "api";; + 4) viewSpecificLogs "worker";; + 5) viewSpecificLogs "beat-worker";; + 6) viewSpecificLogs "migrator";; + 7) viewSpecificLogs "proxy";; + 8) viewSpecificLogs "plane-redis";; + 9) viewSpecificLogs "plane-db";; + 10) viewSpecificLogs "plane-minio";; + 11) viewSpecificLogs "plane-mq";; + 0) askForAction;; + *) echo "INVALID SERVICE NAME SUPPLIED";; + esac + fi + elif [ -n "$ARG_SERVICE_NAME" ]; + then + ARG_SERVICE_NAME=$(echo "$ARG_SERVICE_NAME" | tr '[:upper:]' '[:lower:]') + case $ARG_SERVICE_NAME in + web) viewSpecificLogs "web";; + space) viewSpecificLogs "space";; + api) viewSpecificLogs "api";; + worker) viewSpecificLogs "worker";; + beat-worker) viewSpecificLogs "beat-worker";; + migrator) viewSpecificLogs "migrator";; + proxy) viewSpecificLogs "proxy";; + redis) viewSpecificLogs "plane-redis";; + postgres) viewSpecificLogs "plane-db";; + minio) viewSpecificLogs "plane-minio";; + rabbitmq) viewSpecificLogs "plane-mq";; + *) echo "INVALID SERVICE NAME SUPPLIED";; + esac + else + echo "INVALID SERVICE NAME SUPPLIED" + fi +} + + + +function askForAction() { + # Rest of askForAction remains the same but use $stack_name instead of $STACK_NAME + local DEFAULT_ACTION=$1 + + if [ -z "$DEFAULT_ACTION" ]; then + echo + echo "Select an Action you want to perform:" + echo " 1) Deploy Stack" + echo " 2) Remove Stack" + echo " 3) View Stack Status" + echo " 4) Redeploy Stack" + echo " 5) Upgrade" + echo " 6) View Logs" + echo " 7) Exit" + echo + read -p "Action [3]: " ACTION + until [[ -z "$ACTION" || "$ACTION" =~ ^[1-6]$ ]]; do + echo "$ACTION: invalid selection." + read -p "Action [3]: " ACTION + done + + if [ -z "$ACTION" ]; then + ACTION=3 + fi + echo + fi + + if [ "$ACTION" == "1" ] || [ "$DEFAULT_ACTION" == "deploy" ]; then + deployStack + elif [ "$ACTION" == "2" ] || [ "$DEFAULT_ACTION" == "remove" ]; then + removeStack + elif [ "$ACTION" == "3" ] || [ "$DEFAULT_ACTION" == "status" ]; then + viewStatus + elif [ "$ACTION" == "4" ] || [ "$DEFAULT_ACTION" == "redeploy" ]; then + redeployStack + elif [ "$ACTION" == "5" ] || [ "$DEFAULT_ACTION" == "upgrade" ]; then + upgrade + elif [ "$ACTION" == "6" ] || [ "$DEFAULT_ACTION" == "logs" ]; then + viewLogs "$@" + elif [ "$ACTION" == "7" ] || [ "$DEFAULT_ACTION" == "exit" ]; then + exit 0 + else + echo "INVALID ACTION SUPPLIED" + fi +} + +# Initialize stack name at script start + +if [ -z "$stack_name" ]; then + readStackName +fi + +# Sync environment variables +if [ -f "$DOCKER_ENV_PATH" ]; then + DOCKERHUB_USER=$(getEnvValue "DOCKERHUB_USER" "$DOCKER_ENV_PATH") + APP_RELEASE=$(getEnvValue "APP_RELEASE" "$DOCKER_ENV_PATH") + + if [ -z "$DOCKERHUB_USER" ]; then + DOCKERHUB_USER=makeplane + updateEnvFile "DOCKERHUB_USER" "$DOCKERHUB_USER" "$DOCKER_ENV_PATH" + fi + + if [ -z "$APP_RELEASE" ]; then + APP_RELEASE=stable + updateEnvFile "APP_RELEASE" "$APP_RELEASE" "$DOCKER_ENV_PATH" + fi +fi + + +# Main execution +print_header +askForAction "$@" diff --git a/deploy/selfhost/variables.env b/deploy/selfhost/variables.env index 78914c3afd7..b10cf11d0de 100644 --- a/deploy/selfhost/variables.env +++ b/deploy/selfhost/variables.env @@ -5,6 +5,9 @@ WEB_REPLICAS=1 SPACE_REPLICAS=1 ADMIN_REPLICAS=1 API_REPLICAS=1 +WORKER_REPLICAS=1 +BEAT_WORKER_REPLICAS=1 +LIVE_REPLICAS=1 NGINX_PORT=80 WEB_URL=http://${APP_DOMAIN}