From 1f3c40400b40fc6da3e651c84f97400a9a010f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20Rosenkr=C3=A4nzer?= Date: Fri, 6 Dec 2024 04:19:55 +0100 Subject: [PATCH] [plane] Add initial plane integration --- lib/plane.sh | 485 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 485 insertions(+) create mode 100644 lib/plane.sh diff --git a/lib/plane.sh b/lib/plane.sh new file mode 100644 index 0000000..458df9f --- /dev/null +++ b/lib/plane.sh @@ -0,0 +1,485 @@ +#!/bin/bash +# +# Plane Service + +PATH=$HOME/.docker/cli-plugins:/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +# FIXME plane's nginx proxy has a few additional rules that we may +# need to port to Traefik: +# add_header X-Content-Type-Options "nosniff" always; +# add_header Referrer-Policy "no-referrer-when-downgrade" always; +# add_header Permissions-Policy "interest-cohort=()" always; +# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; +# add_header X-Forwarded-Proto "$scheme"; +# add_header X-Forwarded-Host "$host"; +# add_header X-Forwarded-For "$proxy_add_x_forwarded_for"; +# add_header X-Real-IP "$remote_addr"; + +config_plane() { + echo -ne "\n* Configuring /federated/apps/plane container.." + + if [ ! -d "/federated/apps/plane" ]; then + mkdir -p /federated/apps/plane/data/plane &> /dev/null + fi + + POSTGRES_PASSWORD=$(create_password) + + USE_TRAEFIK=true + + cat >/federated/apps/plane/plane.env <<'EOF' +APP_DOMAIN=plane.@DOMAIN@ +APP_RELEASE=stable + +WEB_REPLICAS=1 +SPACE_REPLICAS=1 +ADMIN_REPLICAS=1 +API_REPLICAS=1 + +NGINX_PORT=80 +WEB_URL=http://${APP_DOMAIN} +DEBUG=0 +SENTRY_DSN= +SENTRY_ENVIRONMENT=production +CORS_ALLOWED_ORIGINS=http://${APP_DOMAIN} +API_BASE_URL=http://api:8000 + +#DB SETTINGS +PGHOST=postgresql +PGDATABASE=plane +POSTGRES_USER=plane +POSTGRES_PASSWORD=@POSTGRES_PASSWORD@ +POSTGRES_DB=plane +POSTGRES_PORT=5432 +PGDATA=/var/lib/postgresql/data +DATABASE_URL= + +# REDIS SETTINGS +REDIS_HOST=plane-redis +REDIS_PORT=6379 +REDIS_URL= + +# RabbitMQ Settings +RABBITMQ_HOST=plane-mq +RABBITMQ_PORT=5672 +RABBITMQ_USER=plane +RABBITMQ_PASSWORD=plane +RABBITMQ_VHOST=plane +AMQP_URL= + +# Secret Key +SECRET_KEY=60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5 + +# DATA STORE SETTINGS +USE_MINIO=1 +AWS_REGION= +AWS_ACCESS_KEY_ID=access-key +AWS_SECRET_ACCESS_KEY=secret-key +AWS_S3_ENDPOINT_URL=http://plane-minio:9000 +AWS_S3_BUCKET_NAME=uploads +MINIO_ROOT_USER=access-key +MINIO_ROOT_PASSWORD=secret-key +BUCKET_NAME=uploads +FILE_SIZE_LIMIT=5242880 + +# Gunicorn Workers +GUNICORN_WORKERS=1 + +# UNCOMMENT `DOCKER_PLATFORM` IF YOU ARE ON `ARM64` AND DOCKER IMAGE IS NOT AVAILABLE FOR RESPECTIVE `APP_RELEASE` +# DOCKER_PLATFORM=linux/amd64 + +DOCKERHUB_USER=makeplane +PULL_POLICY=if_not_present +CUSTOM_BUILD=false +EOF + + cat > /federated/apps/plane/docker-compose.yml <<'EOF' +x-app-env: &app-env + environment: + - NGINX_PORT=${NGINX_PORT:-80} + - WEB_URL=${WEB_URL:-http://localhost} + - DEBUG=${DEBUG:-0} + - SENTRY_DSN=${SENTRY_DSN:-""} + - SENTRY_ENVIRONMENT=${SENTRY_ENVIRONMENT:-"production"} + - CORS_ALLOWED_ORIGINS=${CORS_ALLOWED_ORIGINS:-} + # Gunicorn Workers + - GUNICORN_WORKERS=${GUNICORN_WORKERS:-1} + #DB SETTINGS + - PGHOST=${PGHOST:-postgresql} + - PGDATABASE=${PGDATABASE:-plane} + - POSTGRES_USER=${POSTGRES_USER:-plane} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-@POSTGRES_PASSWORD@} + - POSTGRES_DB=${POSTGRES_DB:-plane} + - POSTGRES_PORT=${POSTGRES_PORT:-5432} + - PGDATA=${PGDATA:-/var/lib/postgresql/data} + - DATABASE_URL=${DATABASE_URL:-postgresql://plane:@POSTGRES_PASSWORD@@postgresql/plane} + # REDIS SETTINGS + - REDIS_HOST=${REDIS_HOST:-plane-redis} + - REDIS_PORT=${REDIS_PORT:-6379} + - REDIS_URL=${REDIS_URL:-redis://plane-redis:6379/} + + # RabbitMQ Settings + - RABBITMQ_HOST=${RABBITMQ_HOST:-plane-mq} + - RABBITMQ_PORT=${RABBITMQ_PORT:-5672} + - RABBITMQ_DEFAULT_USER=${RABBITMQ_USER:-plane} + - RABBITMQ_DEFAULT_PASS=${RABBITMQ_PASSWORD:-plane} + - RABBITMQ_DEFAULT_VHOST=${RABBITMQ_VHOST:-plane} + - RABBITMQ_VHOST=${RABBITMQ_VHOST:-plane} + - AMQP_URL=${AMQP_URL:-amqp://plane:plane@plane-mq:5672/plane} + # Application secret + - SECRET_KEY=${SECRET_KEY:-60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5} + # DATA STORE SETTINGS + - USE_MINIO=${USE_MINIO:-1} + - AWS_REGION=${AWS_REGION:-} + - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-"access-key"} + - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-"secret-key"} + - AWS_S3_ENDPOINT_URL=${AWS_S3_ENDPOINT_URL:-http://plane-minio:9000} + - AWS_S3_BUCKET_NAME=${AWS_S3_BUCKET_NAME:-uploads} + - MINIO_ROOT_USER=${MINIO_ROOT_USER:-"access-key"} + - MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-"secret-key"} + - BUCKET_NAME=${BUCKET_NAME:-uploads} + - FILE_SIZE_LIMIT=${FILE_SIZE_LIMIT:-5242880} + # Live server env + - API_BASE_URL=${API_BASE_URL:-http://api:8000} + +services: + web: + <<: *app-env + image: ${DOCKERHUB_USER:-makeplane}/plane-frontend:${APP_RELEASE:-stable} + platform: ${DOCKER_PLATFORM:-} + expose: + - 3000 + ports: + - 3000:3000 + env_file: + - ./plane.env +EOF + + if $USE_TRAEFIK; then + cat >> /federated/apps/plane/docker-compose.yml <<'EOF' + labels: + - "traefik.enable=true" + - "traefik.http.routers.web.rule=Host(`plane.@DOMAIN@`)" + - "traefik.http.routers.web.entrypoints=websecure" + - "traefik.http.routers.web.tls.certresolver=letsencrypt" + - "traefik.http.services.web.loadbalancer.server.port=3000" +EOF + fi + + cat >> /federated/apps/plane/docker-compose.yml <<'EOF' + networks: + core: + ipv4_address: 192.168.0.50 + pull_policy: if_not_present + restart: unless-stopped + command: node web/server.js web + deploy: + replicas: ${WEB_REPLICAS:-1} + depends_on: + - api + - worker + + space: + <<: *app-env + image: ${DOCKERHUB_USER:-makeplane}/plane-space:${APP_RELEASE:-stable} + platform: ${DOCKER_PLATFORM:-} + env_file: + - ./plane.env + networks: + core: + ipv4_address: 192.168.0.51 +EOF + + if $USE_TRAEFIK; then + cat >> /federated/apps/plane/docker-compose.yml <<'EOF' + labels: + - "traefik.enable=true" + - "traefik.http.routers.space.rule=Host(`plane.@DOMAIN@`) && PathPrefix(`/spaces`)" + - "traefik.http.routers.space.entrypoints=websecure" + - "traefik.http.routers.space.tls.certresolver=letsencrypt" + - "traefik.http.services.space.loadbalancer.server.port=3000" +EOF + fi + + cat >> /federated/apps/plane/docker-compose.yml <<'EOF' + pull_policy: if_not_present + restart: unless-stopped + command: node space/server.js space + deploy: + replicas: ${SPACE_REPLICAS:-1} + depends_on: + - api + - worker + - web + + admin: + <<: *app-env + image: ${DOCKERHUB_USER:-makeplane}/plane-admin:${APP_RELEASE:-stable} + platform: ${DOCKER_PLATFORM:-} + env_file: + - ./plane.env + networks: + core: + ipv4_address: 192.168.0.52 +EOF + + if $USE_TRAEFIK; then + cat >> /federated/apps/plane/docker-compose.yml <<'EOF' + labels: + - "traefik.enable=true" + - "traefik.http.routers.admin.rule=Host(`plane.@DOMAIN@`) && PathPrefix(`/god-mode`)" + - "traefik.http.routers.admin.entrypoints=websecure" + - "traefik.http.routers.admin.tls.certresolver=letsencrypt" + - "traefik.http.services.admin.loadbalancer.server.port=3000" +EOF + fi + + cat >> /federated/apps/plane/docker-compose.yml <<'EOF' + pull_policy: if_not_present + restart: unless-stopped + command: node admin/server.js admin + deploy: + replicas: ${ADMIN_REPLICAS:-1} + depends_on: + - api + - web + + live: + <<: *app-env + image: ${DOCKERHUB_USER:-makeplane}/plane-live:${APP_RELEASE:-stable} + platform: ${DOCKER_PLATFORM:-} + env_file: + - ./plane.env + networks: + core: + ipv4_address: 192.168.0.53 +EOF + + if $USE_TRAEFIK; then + cat >> /federated/apps/plane/docker-compose.yml <<'EOF' + labels: + - "traefik.enable=true" + - "traefik.http.routers.live.rule=Host(`plane.@DOMAIN@`) && PathPrefix(`/live`)" + - "traefik.http.routers.live.entrypoints=websecure" + - "traefik.http.routers.live.tls.certresolver=letsencrypt" + - "traefik.http.services.live.loadbalancer.server.port=3000" +EOF + fi + + cat >> /federated/apps/plane/docker-compose.yml <<'EOF' + pull_policy: if_not_present + restart: unless-stopped + command: node live/dist/server.js live + deploy: + replicas: ${LIVE_REPLICAS:-1} + depends_on: + - api + - web + + api: + <<: *app-env + image: ${DOCKERHUB_USER:-makeplane}/plane-backend:${APP_RELEASE:-stable} + platform: ${DOCKER_PLATFORM:-} + env_file: + - ./plane.env + networks: + core: + ipv4_address: 192.168.0.54 +EOF + + if $USE_TRAEFIK; then + cat >> /federated/apps/plane/docker-compose.yml <<'EOF' + labels: + - "traefik.enable=true" + - "traefik.http.routers.api.rule=Host(`plane.@DOMAIN@`) && (PathPrefix(`/api`) || PathPrefix(`/auth`))" + - "traefik.http.routers.api.entrypoints=websecure" + - "traefik.http.routers.api.tls.certresolver=letsencrypt" + - "traefik.http.services.api.loadbalancer.server.port=8000" +EOF + fi + + cat >> /federated/apps/plane/docker-compose.yml <<'EOF' + pull_policy: if_not_present + restart: unless-stopped + command: ./bin/docker-entrypoint-api.sh + deploy: + replicas: ${API_REPLICAS:-1} + volumes: + - logs_api:/code/plane/logs + depends_on: + - plane-redis + - plane-mq + + + worker: + <<: *app-env + image: ${DOCKERHUB_USER:-makeplane}/plane-backend:${APP_RELEASE:-stable} + platform: ${DOCKER_PLATFORM:-} + env_file: + - ./plane.env + networks: + core: + ipv4_address: 192.168.0.55 + pull_policy: if_not_present + restart: unless-stopped + command: ./bin/docker-entrypoint-worker.sh + volumes: + - logs_worker:/code/plane/logs + depends_on: + - api + - plane-redis + - plane-mq + + beat-worker: + <<: *app-env + image: ${DOCKERHUB_USER:-makeplane}/plane-backend:${APP_RELEASE:-stable} + platform: ${DOCKER_PLATFORM:-} + env_file: + - ./plane.env + networks: + core: + ipv4_address: 192.168.0.56 + pull_policy: if_not_present + restart: unless-stopped + command: ./bin/docker-entrypoint-beat.sh + volumes: + - logs_beat-worker:/code/plane/logs + depends_on: + - api + - plane-redis + - plane-mq + + migrator: + <<: *app-env + image: ${DOCKERHUB_USER:-makeplane}/plane-backend:${APP_RELEASE:-stable} + platform: ${DOCKER_PLATFORM:-} + env_file: + - ./plane.env + networks: + core: + ipv4_address: 192.168.0.57 + pull_policy: if_not_present + restart: "no" + command: ./bin/docker-entrypoint-migrator.sh + volumes: + - logs_migrator:/code/plane/logs + depends_on: + - plane-redis + + plane-redis: + <<: *app-env + image: valkey/valkey:7.2.5-alpine + env_file: + - ./plane.env + networks: + core: + ipv4_address: 192.168.0.58 + pull_policy: if_not_present + restart: unless-stopped + volumes: + - redisdata:/data + + plane-mq: + <<: *app-env + image: rabbitmq:3.13.6-management-alpine + env_file: + - ./plane.env + networks: + core: + ipv4_address: 192.168.0.59 + restart: always + volumes: + - rabbitmq_data:/var/lib/rabbitmq + + plane-minio: + <<: *app-env + image: minio/minio:latest + env_file: + - ./plane.env + networks: + core: + ipv4_address: 192.168.0.60 +EOF + + if $USE_TRAEFIK; then + cat >> /federated/apps/plane/docker-compose.yml <<'EOF' + labels: + - "traefik.enable=true" + - "traefik.http.routers.uploads.rule=Host(`plane.@DOMAIN@`) && PathPrefix(`/uploads`)" + - "traefik.http.routers.uploads.entrypoints=websecure" + - "traefik.http.routers.uploads.tls.certresolver=letsencrypt" + - "traefik.http.services.uploads.loadbalancer.server.port=9000" +EOF + fi + + cat >> /federated/apps/plane/docker-compose.yml <<'EOF' + pull_policy: if_not_present + restart: unless-stopped + command: server /export --console-address ":9090" + volumes: + - uploads:/export +EOF + + if ! $USE_TRAEFIK; then + cat >> /federated/apps/plane/docker-compose.yml <<'EOF' + proxy: + <<: *app-env + image: ${DOCKERHUB_USER:-makeplane}/plane-proxy:${APP_RELEASE:-stable} + env_file: + - ./plane.env + platform: ${DOCKER_PLATFORM:-} + pull_policy: if_not_present + restart: unless-stopped + ports: + - ${NGINX_PORT}:80 + depends_on: + - web + - api + - space +EOF + fi + + cat >> /federated/apps/plane/docker-compose.yml <<'EOF' +volumes: + redisdata: + + uploads: + logs_api: + logs_worker: + logs_beat-worker: + logs_migrator: + rabbitmq_data: + +networks: + core: + external: true +EOF + sed -i -e "s,@DOMAIN@,${DOMAIN},g" \ + -e "s,@POSTGRES_PASSWORD@,${POSTGRES_PASSWORD},g" \ + /federated/apps/plane/docker-compose.yml \ + /federated/apps/plane/plane.env + + set -x + # Create database and user in postgresql + docker exec postgresql psql -U postgres -c "CREATE DATABASE plane" &> /dev/null + docker exec postgresql psql -U postgres -c "CREATE USER plane WITH PASSWORD '${POSTGRES_PASSWORD}'" &> /dev/null + docker exec postgresql psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE plane TO plane" &> /dev/null + set +x + + unset POSTGRES_PASSWORD + + cat > /federated/apps/plane/.env < /dev/null" "7" + echo -ne "done." +} +uninstall_plane() { + docker exec postgresql psql -U postgres -c "DROP DATABASE plane" &> /dev/null + docker exec postgresql psql -U postgres -c "DROP USER plane" &> /dev/null +}