test/lib/plane.sh

548 lines
18 KiB
Bash

#!/bin/bash
#
# Plane Service
PATH=$HOME/.docker/cli-plugins:/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
. /federated/lib/helpers.sh
# 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)
EMAIL_PASSWORD="$(cat /federated/apps/panel/.env |grep ^SMTP_PASSWORD= |cut -d= -f2-)"
USE_TRAEFIK=true
cat >/federated/apps/plane/.env <<'EOF'
APP_DOMAIN=plane.@DOMAIN@
IMAGE_VERSION=v0.24.1
VALKEY_RELEASE=7.2.5-alpine
RABBITMQ_RELEASE=3.13.6-management-alpine
MINIO_RELEASE=RELEASE.2024-12-18T13-15-44Z
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
# Email
EMAIL_HOST=mail.@DOMAIN@
EMAIL_HOST_USER=fcore@@DOMAIN@
EMAIL_HOST_PASSWORD=@EMAIL_PASSWORD@
EMAIL_PORT=587
EMAIL_FROM=admin@@DOMAIN@
EMAIL_USE_TLS=1
EMAIL_USE_SSL=0
# UNCOMMENT `DOCKER_PLATFORM` IF YOU ARE ON `ARM64` AND DOCKER IMAGE IS NOT AVAILABLE FOR RESPECTIVE `IMAGE_VERSION`
# 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:${IMAGE_VERSION:-stable}
platform: ${DOCKER_PLATFORM:-}
expose:
- 3000
ports:
- 3000:3000
env_file:
- .env
EOF
if $USE_TRAEFIK; then
cat >> /federated/apps/plane/docker-compose.yml <<'EOF'
labels:
- "traefik.enable=true"
- "traefik.http.routers.plane-web.rule=Host(`plane.@DOMAIN@`)"
- "traefik.http.routers.plane-web.entrypoints=websecure"
- "traefik.http.routers.plane-web.tls.certresolver=letsencrypt"
- "traefik.http.services.plane-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:${IMAGE_VERSION:-stable}
platform: ${DOCKER_PLATFORM:-}
env_file:
- .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.plane-space.rule=Host(`plane.@DOMAIN@`) && PathPrefix(`/spaces`)"
- "traefik.http.routers.plane-space.entrypoints=websecure"
- "traefik.http.routers.plane-space.tls.certresolver=letsencrypt"
- "traefik.http.services.plane-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:${IMAGE_VERSION:-stable}
platform: ${DOCKER_PLATFORM:-}
env_file:
- .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.plane-admin.rule=Host(`plane.@DOMAIN@`) && PathPrefix(`/god-mode`)"
- "traefik.http.routers.plane-admin.entrypoints=websecure"
- "traefik.http.routers.plane-admin.tls.certresolver=letsencrypt"
- "traefik.http.services.plane-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:${IMAGE_VERSION:-stable}
platform: ${DOCKER_PLATFORM:-}
env_file:
- .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.plane-live.rule=Host(`plane.@DOMAIN@`) && PathPrefix(`/live`)"
- "traefik.http.routers.plane-live.entrypoints=websecure"
- "traefik.http.routers.plane-live.tls.certresolver=letsencrypt"
- "traefik.http.services.plane-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:${IMAGE_VERSION:-stable}
platform: ${DOCKER_PLATFORM:-}
env_file:
- .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.plane-api.rule=Host(`plane.@DOMAIN@`) && (PathPrefix(`/api`) || PathPrefix(`/auth`))"
- "traefik.http.routers.plane-api.entrypoints=websecure"
- "traefik.http.routers.plane-api.tls.certresolver=letsencrypt"
- "traefik.http.services.plane-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:${IMAGE_VERSION:-stable}
platform: ${DOCKER_PLATFORM:-}
env_file:
- .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:${IMAGE_VERSION:-stable}
platform: ${DOCKER_PLATFORM:-}
env_file:
- .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:${IMAGE_VERSION:-stable}
platform: ${DOCKER_PLATFORM:-}
env_file:
- .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:${VALKEY_RELEASE:-7.2.5-alpine}
env_file:
- .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:${RABBITMQ_RELEASE:-3.13.6-management-alpine}
env_file:
- .env
networks:
core:
ipv4_address: 192.168.0.59
restart: always
volumes:
- rabbitmq_data:/var/lib/rabbitmq
plane-minio:
<<: *app-env
image: minio/minio:${MINIO_RELEASE:-RELEASE.2024-12-18T13-15-44Z}
env_file:
- .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.plane-uploads.rule=Host(`plane.@DOMAIN@`) && PathPrefix(`/uploads`)"
- "traefik.http.routers.plane-uploads.entrypoints=websecure"
- "traefik.http.routers.plane-uploads.tls.certresolver=letsencrypt"
- "traefik.http.services.plane-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:${IMAGE_VERSION:-stable}
env_file:
- .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" \
-e "s,@EMAIL_PASSWORD@,${EMAIL_PASSWORD},g" \
/federated/apps/plane/docker-compose.yml \
/federated/apps/plane/.env
chmod 600 /federated/apps/plane/.env
# Create database and user in postgresql
SQL="docker exec postgresql psql --csv -U postgres"
$SQL -c "CREATE DATABASE plane" &> /dev/null
$SQL -c "CREATE USER plane WITH PASSWORD '${POSTGRES_PASSWORD}'" &> /dev/null
$SQL -c "GRANT ALL PRIVILEGES ON DATABASE plane TO plane" &> /dev/null
unset POSTGRES_PASSWORD
# migrator is usually started at the same time as plane - we need to
# run it manually once to create the initial database so we can make
# modifications to it (like creating the admin user) before plane is
# run the regular way
pushd /federated/apps/plane
docker compose up -d migrator
popd
# Wait for the migrator to exit -- at that point, the database should be
# ready for manipulation
echo "Waiting for completion of the initial plane database - this will take some time."
while [ -n "$(docker ps -q -f name=plane-migrator-1)" ]; do
sleep 1s
echo -n .
done
echo
dnf -y --refresh install python-passlib || (apt update ; apt -y install python3-passlib)
INSTANCE_ID=$(random xxxxxxxxxxxxxxxxxxxxxxxx)
INSTANCE_UUID=$(random xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
USER_UUID=$(random xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
ADMIN_UUID=$(random xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
NOTIFICATION_UUID=$(random xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
PROFILE_UUID=$(random xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
SIGNUP_UUID=$(random xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
TOKEN=$(random xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)
ADMINPASS=$(grep ADMINPASS= /var/lib/cloud/instances/[0-9]*/cloud-config.txt |cut -d= -f2- |cut -d'"' -f2 |tr -d '\\')
ENCODED_PASSWORD=$(python3 -c "from passlib.hash import django_pbkdf2_sha256; print(django_pbkdf2_sha256.hash('$ADMINPASS'))")
# FIXME don't hardcode 0.24.0
cat >/federated/apps/postgresql/data/var/lib/postgresql/data/plane-initial-user.sql <<EOF
INSERT INTO instances(created_at, updated_at, id, instance_name, instance_id, current_version, last_checked_at, is_telemetry_enabled, is_support_required, is_setup_done, is_signup_screen_visited, is_verified, domain, latest_version, edition, is_test)
VALUES(NOW(), NOW(), '$INSTANCE_UUID', 'Federated Computer - Plane', '$INSTANCE_ID', '0.24.0', NOW(), FALSE, FALSE, TRUE, TRUE, TRUE, '$DOMAIN', '0.24.0', 'PLANE_COMMUNITY', FALSE);
UPDATE instances SET instance_name='Federated Computer - Plane';
UPDATE instances SET is_telemetry_enabled=FALSE;
UPDATE instances SET is_setup_done=TRUE;
INSERT INTO users (password, last_login, id, username, email, first_name, last_name, avatar, date_joined, created_at, updated_at, last_location, created_location, is_superuser, is_managed, is_password_expired, is_active, is_staff, is_email_verified, is_password_autoset, token, user_timezone, last_active, last_login_time, last_logout_time, last_login_ip, last_logout_ip, last_login_medium, last_login_uagent, is_bot, display_name)
VALUES('$ENCODED_PASSWORD', NOW(), '$USER_UUID', 'admin', 'admin@$DOMAIN', 'Plane', 'Admin', '', NOW(), NOW(), NOW(), '', '', TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, '$TOKEN', 'UTC', NOW(), NOW(), NOW(), '192.168.0.13', '192.168.0.13', 'email', 'Federated Signup/1.0', FALSE, 'admin');
INSERT INTO user_notification_preferences(created_at, updated_at, id, property_change, state_change, comment, mention, issue_completed, user_id)
VALUES(NOW(), NOW(), '$NOTIFICATION_UUID', FALSE, FALSE, FALSE, FALSE, FALSE, '$USER_UUID');
INSERT INTO profiles(created_at, updated_at, id, theme, is_tour_completed, onboarding_step, use_case, role, is_onboarded, billing_address_country, has_billing_address, company_name, user_id)
VALUES(NOW(), NOW(), '$PROFILE_UUID', '{}', FALSE, '{"workspace_join": false, "profile_complete": false, "workspace_create": false, "workspace_invite": false}', '', '', FALSE, 'INDIA', FALSE, '$DOMAIN', '$USER_UUID');
INSERT INTO instance_admins(created_at, updated_at, id, role, is_verified, instance_id, user_id)
VALUES(NOW(), NOW(), '$ADMIN_UUID', 20, TRUE, '$INSTANCE_UUID', '$USER_UUID');
INSERT INTO instance_configurations(created_at, updated_at, id, key, value, category, is_encrypted)
VALUES(NOW(), NOW(), '$SIGNUP_UUID', 'ENABLE_SIGNUP', '0', 'AUTHENTICATION', FALSE);
UPDATE instance_configurations SET value=0 WHERE key='ENABLE_SIGNUP';
EOF
$SQL plane -f /var/lib/postgresql/data/plane-initial-user.sql
# rm /federated/apps/postgresql/data/var/lib/postgresql/data/plane-initial-user.sql
echo -ne "done."
}
start_plane() {
# Start service with command to make sure it's up before proceeding
# start_service "plane" "nc -z 192.168.0.48 80 &> /dev/null" "7"
/federated/bin/start plane
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
rm -rf /federated/apps/plane
}