outline of what should be a working recipe
This commit is contained in:
parent
090874acca
commit
28b67afc2b
18
.env.sample
18
.env.sample
@ -2,10 +2,22 @@ TYPE=zulip
|
||||
|
||||
DOMAIN=zulip.example.com
|
||||
|
||||
## Domain aliases
|
||||
#EXTRA_DOMAINS=', `www.zulip.example.com`'
|
||||
|
||||
LETS_ENCRYPT_ENV=production
|
||||
|
||||
SECRET_DB_PASSWORD_VERSION=v1
|
||||
SECRET_RABBITMQ_PASSWORD_VERSION=v1
|
||||
SECRET_REDIS_PASSWORD_VERSION=v1
|
||||
SECRET_SMTP_PASSWORD_VERSION=v1
|
||||
SECRET_ZULIP_SECRET_VERSION=v1
|
||||
SECRET_MEMCACHED_PASSWORD_VERSION=v1
|
||||
|
||||
#######################
|
||||
##### SMTP CONFIG #####
|
||||
#######################
|
||||
|
||||
SETTING_EMAIL_HOST: "smtp.example.com"
|
||||
SETTING_EMAIL_HOST_USER: "user"
|
||||
SETTING_EMAIL_PORT: "587" # STARTTLS = 587 SSL/TLS = 465
|
||||
|
||||
#SETTING_EMAIL_USE_SSL: "False" # implicit SSL/TLS
|
||||
SETTING_EMAIL_USE_TLS: "True" # STARTTLS
|
||||
|
12
README.md
12
README.md
@ -5,9 +5,9 @@
|
||||
<!-- metadata -->
|
||||
|
||||
* **Category**: Apps
|
||||
* **Status**: 0
|
||||
* **Status**: wip
|
||||
* **Image**: [`zulip`](https://hub.docker.com/r/zulip), 4, upstream
|
||||
* **Healthcheck**: No
|
||||
* **Healthcheck**: Yes
|
||||
* **Backups**: No
|
||||
* **Email**: No
|
||||
* **Tests**: No
|
||||
@ -17,8 +17,12 @@
|
||||
|
||||
## Quick start
|
||||
|
||||
* `abra app new zulip --secrets`
|
||||
* `abra app new zulip`
|
||||
* `abra app secret insert <app-domain> smtp_password v1 <smtp-password>` (A working SMTP server is required for setup)
|
||||
* `abra app config <app-name>`
|
||||
* Populate SMTP settings by editing env variables that start with `SETTING_EMAIL`
|
||||
* `abra app deploy <app-name>`
|
||||
|
||||
For more, see [`docs.coopcloud.tech`](https://docs.coopcloud.tech).
|
||||
|
||||
|
||||
For more, reed [`zulip's documentation`](https://zulip.readthedocs.io/en/latest/index.html).
|
||||
|
5
abra.sh
Normal file
5
abra.sh
Normal file
@ -0,0 +1,5 @@
|
||||
export ENTRYPOINT_VERSION=v1
|
||||
export PG_BACKUP_VERSION=v1
|
||||
export MEM_ENTRYPOINT_VERSION=v1
|
||||
export REDIS_ENTRYPOINT_VERSION=v1
|
||||
export RABBIT_HEALTHCHECK_VERSION=v1
|
79
compose.yml
79
compose.yml
@ -5,8 +5,11 @@ services:
|
||||
|
||||
app:
|
||||
image: "zulip/docker-zulip:9.4-0"
|
||||
ports:
|
||||
- "80:80"
|
||||
configs:
|
||||
- source: entrypoint
|
||||
target: /custom-entrypoint.sh
|
||||
mode: 555
|
||||
entrypoint: /custom-entrypoint.sh
|
||||
environment:
|
||||
DB_HOST: "database"
|
||||
DB_HOST_PORT: "5432"
|
||||
@ -15,19 +18,8 @@ services:
|
||||
SETTING_MEMCACHED_LOCATION: "memcached:11211"
|
||||
SETTING_RABBITMQ_HOST: "rabbitmq"
|
||||
SETTING_REDIS_HOST: "redis"
|
||||
SECRETS_email_password: "123456789"
|
||||
SECRETS_rabbitmq_password: "REPLACE_WITH_SECURE_RABBITMQ_PASSWORD"
|
||||
SECRETS_postgres_password: "REPLACE_WITH_SECURE_POSTGRES_PASSWORD"
|
||||
SECRETS_memcached_password: "REPLACE_WITH_SECURE_MEMCACHED_PASSWORD"
|
||||
SECRETS_redis_password: "REPLACE_WITH_SECURE_REDIS_PASSWORD"
|
||||
SECRETS_secret_key: "REPLACE_WITH_SECURE_SECRET_KEY"
|
||||
SETTING_EXTERNAL_HOST: "localhost.localdomain"
|
||||
SETTING_EXTERNAL_HOST: ${DOMAIN}
|
||||
SETTING_ZULIP_ADMINISTRATOR: "admin@example.com"
|
||||
SETTING_EMAIL_HOST: ""
|
||||
SETTING_EMAIL_HOST_USER: "noreply@example.com"
|
||||
SETTING_EMAIL_PORT: "587"
|
||||
SETTING_EMAIL_USE_SSL: "False"
|
||||
SETTING_EMAIL_USE_TLS: "True"
|
||||
ZULIP_AUTH_BACKENDS: "EmailAuthBackend"
|
||||
volumes:
|
||||
- "zulip:/data:rw"
|
||||
@ -56,13 +48,13 @@ services:
|
||||
configs:
|
||||
- source: pg_backup
|
||||
target: /pg_backup.sh
|
||||
mode: 0555
|
||||
mode: 555
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "pg_isready" ]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
|
||||
memcached:
|
||||
image: "memcached:alpine"
|
||||
command:
|
||||
@ -73,35 +65,53 @@ services:
|
||||
echo "zulip@$$HOSTNAME:$$MEMCACHED_PASSWORD" > "$$MEMCACHED_SASL_PWDB"
|
||||
echo "zulip@localhost:$$MEMCACHED_PASSWORD" >> "$$MEMCACHED_SASL_PWDB"
|
||||
exec memcached -S
|
||||
configs:
|
||||
- source: memcached_entrypoint
|
||||
target: /custom-entrypoint.sh
|
||||
mode: 555
|
||||
entrypoint: /custom-entrypoint.sh
|
||||
secrets:
|
||||
- memcached_password
|
||||
environment:
|
||||
SASL_CONF_PATH: "/home/memcache/memcached.conf"
|
||||
MEMCACHED_SASL_PWDB: "/home/memcache/memcached-sasl-db"
|
||||
MEMCACHED_PASSWORD: "REPLACE_WITH_SECURE_MEMCACHED_PASSWORD"
|
||||
|
||||
rabbitmq:
|
||||
image: "rabbitmq:3.12.14"
|
||||
environment:
|
||||
RABBITMQ_DEFAULT_USER: "zulip"
|
||||
RABBITMQ_DEFAULT_PASS_FILE: "/run/secrets/rabbitmq_password"
|
||||
configs:
|
||||
- source: rabbitmq_healthcheck
|
||||
target: /healthcheck.sh
|
||||
mode: 555
|
||||
secrets:
|
||||
- rabbitmq_password
|
||||
volumes:
|
||||
- "rabbitmq:/var/lib/rabbitmq:rw"
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "/healthcheck.sh" ]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
redis:
|
||||
image: "redis:alpine"
|
||||
configs:
|
||||
- source: redis_entrypoint
|
||||
target: /custom-entrypoint.sh
|
||||
mode: 555
|
||||
entrypoint: /custom-entrypoint.sh
|
||||
command:
|
||||
- "sh"
|
||||
- "-euc"
|
||||
- |
|
||||
echo "requirepass '$$REDIS_PASSWORD'" > /etc/redis.conf
|
||||
exec redis-server /etc/redis.conf
|
||||
environment:
|
||||
REDIS_PASSWORD: "REPLACE_WITH_SECURE_REDIS_PASSWORD"
|
||||
environment: REDIS_PASSWORD
|
||||
volumes:
|
||||
- "redis:/data:rw"
|
||||
|
||||
|
||||
secrets:
|
||||
db_password:
|
||||
name: ${STACK_NAME}_db_password_${SECRET_DB_PASSWORD_VERSION}
|
||||
@ -109,11 +119,39 @@ secrets:
|
||||
rabbitmq_password:
|
||||
name: ${STACK_NAME}_rabbitmq_password_${SECRET_RABBITMQ_PASSWORD_VERSION}
|
||||
external: true
|
||||
redis_password:
|
||||
name: ${STACK_NAME}_redis_password_${SECRET_REDIS_PASSWORD_VERSION}
|
||||
external: true
|
||||
memcached_password:
|
||||
name: ${STACK_NAME}_memcached_password_${SECRET_MEMCACHED_PASSWORD_VERSION}
|
||||
external: true
|
||||
smtp_password:
|
||||
name: ${STACK_NAME}_smtp_password_${SECRET_SMTP_PASSWORD_VERSION}
|
||||
external: true
|
||||
zulip_secret:
|
||||
name: ${STACK_NAME}_zulip_secret_${SECRET_ZULIP_SECRET_VERSION}
|
||||
external: true
|
||||
|
||||
|
||||
configs:
|
||||
pg_backup:
|
||||
name: ${STACK_NAME}_pg_backup_${PG_BACKUP_VERSION}
|
||||
file: pg_backup.sh
|
||||
entrypoint:
|
||||
name: ${STACK_NAME}_entrypoint_${ENTRYPOINT_VERSION}
|
||||
file: entrypoint.sh.tmpl
|
||||
template_driver: golang
|
||||
memcached_entrypoint:
|
||||
name: ${STACK_NAME}_memcached_entrypoint_${MEM_ENTRYPOINT_VERSION}
|
||||
file: entrypoint.memcached.sh.tmpl
|
||||
template_driver: golang
|
||||
redis_entrypoint:
|
||||
name: ${STACK_NAME}_redis_entrypoint_${REDIS_ENTRYPOINT_VERSION}
|
||||
file: entrypoint.redis.sh.tmpl
|
||||
template_driver: golang
|
||||
rabbitmq_healthcheck:
|
||||
name: ${STACK_NAME}_rabbitmq_healthcheck_${RABBIT_HEALTHCHECK_VERSION}
|
||||
file: rabbitmq_healthcheck.sh
|
||||
|
||||
volumes:
|
||||
zulip:
|
||||
@ -121,6 +159,7 @@ volumes:
|
||||
rabbitmq:
|
||||
redis:
|
||||
|
||||
|
||||
networks:
|
||||
internal:
|
||||
proxy:
|
||||
|
17
entryopoint.memcached.sh.tmpl
Normal file
17
entryopoint.memcached.sh.tmpl
Normal file
@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ -f /run/secrets/memcached_password ]; then
|
||||
export MEMCACHED_PASSWORD=$(cat /run/secrets/memcached_password)
|
||||
else
|
||||
echo "Membcached password secret not found, skipping."
|
||||
fi
|
||||
|
||||
|
||||
set -e
|
||||
|
||||
# first arg is `-f` or `--some-option`
|
||||
if [ "${1#-}" != "$1" ]; then
|
||||
set -- memcached "$@"
|
||||
fi
|
||||
|
||||
exec "$@"
|
45
entrypoint.redis.sh.tmpl
Normal file
45
entrypoint.redis.sh.tmpl
Normal file
@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -f /run/secrets/redis_password ]; then
|
||||
export REDIS_PASSWORD=$(cat /run/secrets/redis_password)
|
||||
else
|
||||
echo "REDIS secret not found, skipping."
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
### docker entrypoint script, for starting redis stack
|
||||
BASEDIR=/opt/redis-stack
|
||||
cd ${BASEDIR}
|
||||
|
||||
CMD=${BASEDIR}/bin/redis-server
|
||||
if [ -f /redis-stack.conf ]; then
|
||||
CONFFILE=/redis-stack.conf
|
||||
fi
|
||||
|
||||
if [ -z "${REDIS_DATA_DIR}" ]; then
|
||||
REDIS_DATA_DIR=/data
|
||||
fi
|
||||
|
||||
# when running in redis-stack (as opposed to redis-stack-server)
|
||||
if [ -f ${BASEDIR}/nodejs/bin/node ]; then
|
||||
${BASEDIR}/nodejs/bin/node -r ${BASEDIR}/share/redisinsight/api/node_modules/dotenv/config ${BASEDIR}/share/redisinsight/api/dist/src/main.js dotenv_config_path=${BASEDIR}/share/redisinsight/.env &
|
||||
fi
|
||||
|
||||
if [ -z "${REDISEARCH_ARGS}" ]; then
|
||||
REDISEARCH_ARGS="MAXSEARCHRESULTS 10000 MAXAGGREGATERESULTS 10000"
|
||||
fi
|
||||
|
||||
${CMD} \
|
||||
${CONFFILE} \
|
||||
--dir ${REDIS_DATA_DIR} \
|
||||
--protected-mode no \
|
||||
--daemonize no \
|
||||
--loadmodule /opt/redis-stack/lib/rediscompat.so \
|
||||
--loadmodule /opt/redis-stack/lib/redisearch.so ${REDISEARCH_ARGS} \
|
||||
--loadmodule /opt/redis-stack/lib/redistimeseries.so ${REDISTIMESERIES_ARGS} \
|
||||
--loadmodule /opt/redis-stack/lib/rejson.so ${REDISJSON_ARGS} \
|
||||
--loadmodule /opt/redis-stack/lib/redisbloom.so ${REDISBLOOM_ARGS} \
|
||||
--loadmodule /opt/redis-stack/lib/redisgears.so v8-plugin-path /opt/redis-stack/lib/libredisgears_v8_plugin.so ${REDISGEARS_ARGS} \
|
||||
${REDIS_ARGS}
|
630
entrypoint.sh.tmpl
Normal file
630
entrypoint.sh.tmpl
Normal file
@ -0,0 +1,630 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -f /run/secrets/db_password ]; then
|
||||
export SECRETS_postgres_password=$(cat /run/secrets/db_password)
|
||||
else
|
||||
echo "Postgres password secret not found, skipping."
|
||||
fi
|
||||
|
||||
if [ -f /run/secrets/memcached_password ]; then
|
||||
export SECRETS_memcached_password=$(cat /run/secrets/memcached_password)
|
||||
else
|
||||
echo "Membcached password secret not found, skipping."
|
||||
fi
|
||||
|
||||
if [ -f /run/secrets/redis_password ]; then
|
||||
export SECRETS_redis_password=$(cat /run/secrets/redis_password)
|
||||
else
|
||||
echo "DB password secret not found, skipping."
|
||||
fi
|
||||
|
||||
if [ -f /run/secrets/rabbitmq_password ]; then
|
||||
export SECRETS_rabbitmq_password=$(cat /run/secrets/rabbitmq_password)
|
||||
else
|
||||
echo "DB password secret not found, skipping."
|
||||
fi
|
||||
|
||||
if [ -f /run/secrets/smtp_password ]; then
|
||||
export SECRETS_email_password=$(cat /run/secrets/smtp_password)
|
||||
else
|
||||
echo "DB password secret not found, skipping."
|
||||
fi
|
||||
|
||||
if [ -f /run/secrets/zulip_secret ]; then
|
||||
export SECRETS_secret_key=$(cat /run/secrets/zulip_secret)
|
||||
else
|
||||
echo "Zulip secret not found, skipping."
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
if [ "$DEBUG" = "true" ] || [ "$DEBUG" = "True" ]; then
|
||||
set -x
|
||||
set -o functrace
|
||||
fi
|
||||
set -e
|
||||
shopt -s extglob
|
||||
|
||||
# DB aka Database
|
||||
DB_HOST="${DB_HOST:-127.0.0.1}"
|
||||
DB_HOST_PORT="${DB_HOST_PORT:-5432}"
|
||||
DB_NAME="${DB_NAME:-zulip}"
|
||||
DB_USER="${DB_USER:-zulip}"
|
||||
REMOTE_POSTGRES_SSLMODE="${REMOTE_POSTGRES_SSLMODE:-prefer}"
|
||||
# RabbitMQ
|
||||
SETTING_RABBITMQ_HOST="${SETTING_RABBITMQ_HOST:-127.0.0.1}"
|
||||
SETTING_RABBITMQ_USER="${SETTING_RABBITMQ_USER:-zulip}"
|
||||
export RABBITMQ_NODE="$SETTING_RABBITMQ_HOST"
|
||||
# Redis
|
||||
SETTING_RATE_LIMITING="${SETTING_RATE_LIMITING:-True}"
|
||||
SETTING_REDIS_HOST="${SETTING_REDIS_HOST:-127.0.0.1}"
|
||||
SETTING_REDIS_PORT="${SETTING_REDIS_PORT:-6379}"
|
||||
# Memcached
|
||||
if [ -z "$SETTING_MEMCACHED_LOCATION" ]; then
|
||||
SETTING_MEMCACHED_LOCATION="127.0.0.1:11211"
|
||||
fi
|
||||
# Nginx settings
|
||||
DISABLE_HTTPS="${DISABLE_HTTPS:-false}"
|
||||
NGINX_WORKERS="${NGINX_WORKERS:-2}"
|
||||
NGINX_PROXY_BUFFERING="${NGINX_PROXY_BUFFERING:-off}"
|
||||
NGINX_MAX_UPLOAD_SIZE="${NGINX_MAX_UPLOAD_SIZE:-80m}"
|
||||
# Zulip certifcate parameters
|
||||
SSL_CERTIFICATE_GENERATION="${SSL_CERTIFICATE_GENERATION:self-signed}"
|
||||
# Zulip related settings
|
||||
ZULIP_AUTH_BACKENDS="${ZULIP_AUTH_BACKENDS:-EmailAuthBackend}"
|
||||
ZULIP_RUN_POST_SETUP_SCRIPTS="${ZULIP_RUN_POST_SETUP_SCRIPTS:-True}"
|
||||
# Zulip user setup
|
||||
FORCE_FIRST_START_INIT="${FORCE_FIRST_START_INIT:-False}"
|
||||
# Auto backup settings
|
||||
AUTO_BACKUP_ENABLED="${AUTO_BACKUP_ENABLED:-True}"
|
||||
AUTO_BACKUP_INTERVAL="${AUTO_BACKUP_INTERVAL:-30 3 * * *}"
|
||||
# Zulip configuration function specific variable(s)
|
||||
SPECIAL_SETTING_DETECTION_MODE="${SPECIAL_SETTING_DETECTION_MODE:-}"
|
||||
MANUAL_CONFIGURATION="${MANUAL_CONFIGURATION:-false}"
|
||||
LINK_SETTINGS_TO_DATA="${LINK_SETTINGS_TO_DATA:-false}"
|
||||
# entrypoint.sh specific variable(s)
|
||||
SETTINGS_PY="/etc/zulip/settings.py"
|
||||
|
||||
# BEGIN appRun functions
|
||||
# === initialConfiguration ===
|
||||
prepareDirectories() {
|
||||
mkdir -p "$DATA_DIR" "$DATA_DIR/backups" "$DATA_DIR/certs" "$DATA_DIR/letsencrypt" "$DATA_DIR/uploads"
|
||||
[ -e /etc/letsencrypt ] || ln -ns "$DATA_DIR/letsencrypt" /etc/letsencrypt
|
||||
echo "Preparing and linking the uploads folder ..."
|
||||
rm -rf /home/zulip/uploads
|
||||
ln -sfT "$DATA_DIR/uploads" /home/zulip/uploads
|
||||
chown zulip:zulip -R "$DATA_DIR/uploads"
|
||||
# Link settings folder
|
||||
if [ "$LINK_SETTINGS_TO_DATA" = "True" ] || [ "$LINK_SETTINGS_TO_DATA" = "true" ]; then
|
||||
# Create settings directories
|
||||
if [ ! -d "$DATA_DIR/settings" ]; then
|
||||
mkdir -p "$DATA_DIR/settings"
|
||||
fi
|
||||
if [ ! -d "$DATA_DIR/settings/etc-zulip" ]; then
|
||||
cp -rf /etc/zulip "$DATA_DIR/settings/etc-zulip"
|
||||
fi
|
||||
# Link /etc/zulip/ settings folder
|
||||
rm -rf /etc/zulip
|
||||
ln -sfT "$DATA_DIR/settings/etc-zulip" /etc/zulip
|
||||
fi
|
||||
echo "Prepared and linked the uploads directory."
|
||||
}
|
||||
setConfigurationValue() {
|
||||
if [ -z "$1" ]; then
|
||||
echo "No KEY given for setConfigurationValue."
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$3" ]; then
|
||||
echo "No FILE given for setConfigurationValue."
|
||||
return 1
|
||||
fi
|
||||
local KEY="$1"
|
||||
local VALUE
|
||||
local FILE="$3"
|
||||
local TYPE="$4"
|
||||
if [ -z "$TYPE" ]; then
|
||||
case "$2" in
|
||||
[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|[Nn]one)
|
||||
TYPE="bool"
|
||||
;;
|
||||
+([0-9]))
|
||||
TYPE="integer"
|
||||
;;
|
||||
[\[\(]*[\]\)])
|
||||
TYPE="array"
|
||||
;;
|
||||
*)
|
||||
TYPE="string"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
case "$TYPE" in
|
||||
emptyreturn)
|
||||
if [ -z "$2" ]; then
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
literal)
|
||||
VALUE="$1"
|
||||
;;
|
||||
bool|boolean|int|integer|array)
|
||||
VALUE="$KEY = $2"
|
||||
;;
|
||||
string|*)
|
||||
VALUE="$KEY = '${2//\'/\'}'"
|
||||
;;
|
||||
esac
|
||||
echo "$VALUE" >> "$FILE"
|
||||
echo "Setting key \"$KEY\", type \"$TYPE\" in file \"$FILE\"."
|
||||
}
|
||||
nginxConfiguration() {
|
||||
echo "Executing nginx configuration ..."
|
||||
sed -i "s/worker_processes .*/worker_processes $NGINX_WORKERS;/g" /etc/nginx/nginx.conf
|
||||
sed -i "s/client_max_body_size .*/client_max_body_size $NGINX_MAX_UPLOAD_SIZE;/g" /etc/nginx/nginx.conf
|
||||
sed -i "s/proxy_buffering .*/proxy_buffering $NGINX_PROXY_BUFFERING;/g" /etc/nginx/zulip-include/proxy_longpolling
|
||||
echo "Nginx configuration succeeded."
|
||||
}
|
||||
puppetConfiguration() {
|
||||
echo "Executing puppet configuration ..."
|
||||
|
||||
if [ "$DISABLE_HTTPS" == "True" ] || [ "$DISABLE_HTTPS" == "true" ]; then
|
||||
echo "Disabling https in nginx."
|
||||
crudini --set /etc/zulip/zulip.conf application_server http_only true
|
||||
fi
|
||||
if [ "$QUEUE_WORKERS_MULTIPROCESS" == "True" ] || [ "$QUEUE_WORKERS_MULTIPROCESS" == "true" ]; then
|
||||
echo "Setting queue workers to run in multiprocess mode ..."
|
||||
crudini --set /etc/zulip/zulip.conf application_server queue_workers_multiprocess true
|
||||
elif [ "$QUEUE_WORKERS_MULTIPROCESS" == "False" ] || [ "$QUEUE_WORKERS_MULTIPROCESS" == "false" ]; then
|
||||
echo "Setting queue workers to run in multithreaded mode ..."
|
||||
crudini --set /etc/zulip/zulip.conf application_server queue_workers_multiprocess false
|
||||
fi
|
||||
|
||||
if [ -n "$LOADBALANCER_IPS" ]; then
|
||||
echo "Setting IPs for load balancer"
|
||||
crudini --set /etc/zulip/zulip.conf loadbalancer ips "${LOADBALANCER_IPS}"
|
||||
fi
|
||||
|
||||
/home/zulip/deployments/current/scripts/zulip-puppet-apply -f
|
||||
}
|
||||
configureCerts() {
|
||||
case "$SSL_CERTIFICATE_GENERATION" in
|
||||
self-signed)
|
||||
GENERATE_SELF_SIGNED_CERT="True"
|
||||
GENERATE_CERTBOT_CERT="False"
|
||||
;;
|
||||
|
||||
certbot)
|
||||
GENERATE_SELF_SIGNED_CERT="False"
|
||||
GENERATE_CERTBOT_CERT="True"
|
||||
;;
|
||||
*)
|
||||
echo "Not requesting auto-generated self-signed certs."
|
||||
GENERATE_CERTBOT_CERT="False"
|
||||
GENERATE_SELF_SIGNED_CERT="False"
|
||||
;;
|
||||
esac
|
||||
if [ ! -e "$DATA_DIR/certs/zulip.key" ] && [ ! -e "$DATA_DIR/certs/zulip.combined-chain.crt" ]; then
|
||||
|
||||
if [ "$GENERATE_CERTBOT_CERT" = "True" ]; then
|
||||
# Zulip isn't yet running, so the certbot's challenge can't be met.
|
||||
# We'll schedule this for later.
|
||||
echo "Scheduling LetsEncrypt cert generation ..."
|
||||
GENERATE_CERTBOT_CERT_SCHEDULED=True
|
||||
|
||||
# Generate self-signed certs just to get Zulip going.
|
||||
GENERATE_SELF_SIGNED_CERT=True
|
||||
fi
|
||||
|
||||
if [ "$GENERATE_SELF_SIGNED_CERT" = "True" ]; then
|
||||
echo "Generating self-signed certificates ..."
|
||||
mkdir -p "$DATA_DIR/certs"
|
||||
/home/zulip/deployments/current/scripts/setup/generate-self-signed-cert "$SETTING_EXTERNAL_HOST"
|
||||
mv /etc/ssl/private/zulip.key "$DATA_DIR/certs/zulip.key"
|
||||
mv /etc/ssl/certs/zulip.combined-chain.crt "$DATA_DIR/certs/zulip.combined-chain.crt"
|
||||
echo "Self-signed certificate generation succeeded."
|
||||
else
|
||||
echo "Certificates already exist. No need to generate them. Continuing."
|
||||
fi
|
||||
fi
|
||||
if [ ! -e "$DATA_DIR/certs/zulip.key" ]; then
|
||||
echo "SSL private key zulip.key is not present in $DATA_DIR."
|
||||
echo "Certificates configuration failed."
|
||||
echo "Consider setting SSL_CERTIFICATE_GENERATION in the environment to auto-generate"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -e "$DATA_DIR/certs/zulip.combined-chain.crt" ]; then
|
||||
echo "SSL public key zulip.combined-chain.crt is not present in $DATA_DIR."
|
||||
echo "Certificates configuration failed."
|
||||
echo "Consider setting SSL_CERTIFICATE_GENERATION in the environment to auto-generate"
|
||||
exit 1
|
||||
fi
|
||||
ln -sfT "$DATA_DIR/certs/zulip.key" /etc/ssl/private/zulip.key
|
||||
ln -sfT "$DATA_DIR/certs/zulip.combined-chain.crt" /etc/ssl/certs/zulip.combined-chain.crt
|
||||
echo "Certificates configuration succeeded."
|
||||
}
|
||||
secretsConfiguration() {
|
||||
echo "Setting Zulip secrets ..."
|
||||
if [ ! -e "$DATA_DIR/zulip-secrets.conf" ]; then
|
||||
echo "Generating Zulip secrets ..."
|
||||
/root/zulip/scripts/setup/generate_secrets.py --production
|
||||
mv "/etc/zulip/zulip-secrets.conf" "$DATA_DIR/zulip-secrets.conf"
|
||||
ln -ns "$DATA_DIR/zulip-secrets.conf" "/etc/zulip/zulip-secrets.conf"
|
||||
else
|
||||
ln -nsf "$DATA_DIR/zulip-secrets.conf" "/etc/zulip/zulip-secrets.conf"
|
||||
echo "Generating Zulip secrets ..."
|
||||
/root/zulip/scripts/setup/generate_secrets.py --production
|
||||
fi
|
||||
echo "Secrets generation succeeded."
|
||||
local key
|
||||
for key in "${!SECRETS_@}"; do
|
||||
[[ "$key" == SECRETS_*([0-9A-Z_a-z-]) ]] || continue
|
||||
local SECRET_KEY="${key#SECRETS_}"
|
||||
local SECRET_VAR="${!key}"
|
||||
if [ -z "$SECRET_VAR" ]; then
|
||||
echo "Empty secret for key \"$SECRET_KEY\"."
|
||||
fi
|
||||
crudini --set "$DATA_DIR/zulip-secrets.conf" "secrets" "${SECRET_KEY}" "${SECRET_VAR}"
|
||||
done
|
||||
echo "Zulip secrets configuration succeeded."
|
||||
}
|
||||
databaseConfiguration() {
|
||||
echo "Setting database configuration ..."
|
||||
setConfigurationValue "REMOTE_POSTGRES_HOST" "$DB_HOST" "$SETTINGS_PY" "string"
|
||||
setConfigurationValue "REMOTE_POSTGRES_PORT" "$DB_HOST_PORT" "$SETTINGS_PY" "string"
|
||||
setConfigurationValue "REMOTE_POSTGRES_SSLMODE" "$REMOTE_POSTGRES_SSLMODE" "$SETTINGS_PY" "string"
|
||||
# The password will be set in secretsConfiguration
|
||||
echo "Database configuration succeeded."
|
||||
}
|
||||
authenticationBackends() {
|
||||
echo "Activating authentication backends ..."
|
||||
local FIRST=true
|
||||
local auth_backends
|
||||
IFS=, read -r -a auth_backends <<< "$ZULIP_AUTH_BACKENDS"
|
||||
for AUTH_BACKEND in "${auth_backends[@]}"; do
|
||||
if [ "$FIRST" = true ]; then
|
||||
setConfigurationValue "AUTHENTICATION_BACKENDS" "('zproject.backends.${AUTH_BACKEND//\'/\'}',)" "$SETTINGS_PY" "array"
|
||||
FIRST=false
|
||||
else
|
||||
setConfigurationValue "AUTHENTICATION_BACKENDS += ('zproject.backends.${AUTH_BACKEND//\'/\'}',)" "" "$SETTINGS_PY" "literal"
|
||||
fi
|
||||
echo "Adding authentication backend \"$AUTH_BACKEND\"."
|
||||
done
|
||||
echo "Authentication backend activation succeeded."
|
||||
}
|
||||
zulipConfiguration() {
|
||||
echo "Executing Zulip configuration ..."
|
||||
if [ -n "$ZULIP_CUSTOM_SETTINGS" ]; then
|
||||
echo -e "\n$ZULIP_CUSTOM_SETTINGS" >> "$SETTINGS_PY"
|
||||
fi
|
||||
local key
|
||||
for key in "${!SETTING_@}"; do
|
||||
[[ "$key" == SETTING_*([0-9A-Za-z_]) ]] || continue
|
||||
local setting_key="${key#SETTING_}"
|
||||
local setting_var="${!key}"
|
||||
local type="string"
|
||||
if [ -z "$setting_var" ]; then
|
||||
echo "Empty var for key \"$setting_key\"."
|
||||
continue
|
||||
fi
|
||||
# Zulip settings.py / zproject specific overrides here
|
||||
if [ "$setting_key" = "AUTH_LDAP_CONNECTION_OPTIONS" ] || \
|
||||
[ "$setting_key" = "AUTH_LDAP_GLOBAL_OPTIONS" ] || \
|
||||
[ "$setting_key" = "AUTH_LDAP_USER_SEARCH" ] || \
|
||||
[ "$setting_key" = "AUTH_LDAP_GROUP_SEARCH" ] || \
|
||||
[ "$setting_key" = "AUTH_LDAP_REVERSE_EMAIL_SEARCH" ] || \
|
||||
[ "$setting_key" = "AUTH_LDAP_USER_ATTR_MAP" ] || \
|
||||
[ "$setting_key" = "AUTH_LDAP_USER_FLAGS_BY_GROUP" ] || \
|
||||
[ "$setting_key" = "AUTH_LDAP_GROUP_TYPE" ] || \
|
||||
[ "$setting_key" = "AUTH_LDAP_ADVANCED_REALM_ACCESS_CONTROL" ] || \
|
||||
[ "$setting_key" = "LDAP_SYNCHRONIZED_GROUPS_BY_REALM" ] || \
|
||||
[ "$setting_key" = "SOCIAL_AUTH_OIDC_ENABLED_IDPS" ] || \
|
||||
[ "$setting_key" = "SOCIAL_AUTH_SAML_ENABLED_IDPS" ] || \
|
||||
[ "$setting_key" = "SOCIAL_AUTH_SAML_ORG_INFO" ] || \
|
||||
{ [ "$setting_key" = "LDAP_APPEND_DOMAIN" ] && [ "$setting_var" = "None" ]; } || \
|
||||
[ "$setting_key" = "SCIM_CONFIG" ] || \
|
||||
[ "$setting_key" = "SECURE_PROXY_SSL_HEADER" ] || \
|
||||
[[ "$setting_key" = "CSRF_"* ]] || \
|
||||
[ "$setting_key" = "REALM_HOSTS" ] || \
|
||||
[ "$setting_key" = "ALLOWED_HOSTS" ]; then
|
||||
type="array"
|
||||
fi
|
||||
if [ "$SPECIAL_SETTING_DETECTION_MODE" = "True" ] || [ "$SPECIAL_SETTING_DETECTION_MODE" = "true" ] || \
|
||||
[ "$type" = "string" ]; then
|
||||
type=""
|
||||
fi
|
||||
if [ "$setting_key" = "EMAIL_HOST_USER" ] || \
|
||||
[ "$setting_key" = "EMAIL_HOST_PASSWORD" ] || \
|
||||
[ "$setting_key" = "EXTERNAL_HOST" ]; then
|
||||
type="string"
|
||||
fi
|
||||
setConfigurationValue "$setting_key" "$setting_var" "$SETTINGS_PY" "$type"
|
||||
done
|
||||
if ! su zulip -c "/home/zulip/deployments/current/manage.py checkconfig"; then
|
||||
echo "Error in the Zulip configuration. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
echo "Zulip configuration succeeded."
|
||||
}
|
||||
autoBackupConfiguration() {
|
||||
if [ "$AUTO_BACKUP_ENABLED" != "True" ] && [ "$AUTO_BACKUP_ENABLED" != "true" ]; then
|
||||
rm -f /etc/cron.d/autobackup
|
||||
echo "Auto backup is disabled. Continuing."
|
||||
return 0
|
||||
fi
|
||||
printf 'MAILTO=""\n%s cd /;/sbin/entrypoint.sh app:backup\n' "$AUTO_BACKUP_INTERVAL" > /etc/cron.d/autobackup
|
||||
echo "Auto backup enabled."
|
||||
}
|
||||
initialConfiguration() {
|
||||
echo "=== Begin Initial Configuration Phase ==="
|
||||
prepareDirectories
|
||||
puppetConfiguration
|
||||
nginxConfiguration
|
||||
configureCerts
|
||||
if [ "$MANUAL_CONFIGURATION" = "False" ] || [ "$MANUAL_CONFIGURATION" = "false" ]; then
|
||||
# Start with the settings template file.
|
||||
cp -a /home/zulip/deployments/current/zproject/prod_settings_template.py "$SETTINGS_PY"
|
||||
databaseConfiguration
|
||||
secretsConfiguration
|
||||
authenticationBackends
|
||||
zulipConfiguration
|
||||
fi
|
||||
autoBackupConfiguration
|
||||
echo "=== End Initial Configuration Phase ==="
|
||||
}
|
||||
# === bootstrappingEnvironment ===
|
||||
waitingForDatabase() {
|
||||
local TIMEOUT=60
|
||||
echo "Waiting for database server to allow connections ..."
|
||||
while ! PGPASSWORD="${SECRETS_postgres_password?}" /usr/bin/pg_isready -h "$DB_HOST" -p "$DB_HOST_PORT" -U "$DB_USER" -t 1 >/dev/null 2>&1
|
||||
do
|
||||
if ! ((TIMEOUT--)); then
|
||||
echo "Could not connect to database server. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
echo -n "."
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
zulipFirstStartInit() {
|
||||
echo "Executing Zulip first start init ..."
|
||||
if [ -e "$DATA_DIR/.initiated" ] && [ "$FORCE_FIRST_START_INIT" != "True" ] && [ "$FORCE_FIRST_START_INIT" != "true" ]; then
|
||||
echo "First Start Init not needed. Continuing."
|
||||
return 0
|
||||
fi
|
||||
local RETURN_CODE=0
|
||||
set +e
|
||||
su zulip -c /home/zulip/deployments/current/scripts/setup/initialize-database
|
||||
RETURN_CODE=$?
|
||||
if [[ $RETURN_CODE != 0 ]]; then
|
||||
echo "Zulip first start database initi failed in \"initialize-database\" exit code $RETURN_CODE. Exiting."
|
||||
exit $RETURN_CODE
|
||||
fi
|
||||
set -e
|
||||
touch "$DATA_DIR/.initiated"
|
||||
echo "Zulip first start init sucessful."
|
||||
}
|
||||
zulipMigration() {
|
||||
echo "Migrating Zulip to new version ..."
|
||||
set +e
|
||||
su zulip -c "/home/zulip/deployments/current/manage.py migrate --noinput"
|
||||
local RETURN_CODE=$?
|
||||
if [[ $RETURN_CODE != 0 ]]; then
|
||||
echo "Zulip migration failed with exit code $RETURN_CODE. Exiting."
|
||||
exit $RETURN_CODE
|
||||
fi
|
||||
set -e
|
||||
rm -rf "$DATA_DIR/.zulip-*"
|
||||
touch "$DATA_DIR/.zulip-$ZULIP_VERSION"
|
||||
echo "Zulip migration succeeded."
|
||||
}
|
||||
runPostSetupScripts() {
|
||||
echo "Post setup scripts execution ..."
|
||||
if [ "$ZULIP_RUN_POST_SETUP_SCRIPTS" != "True" ] && [ "$ZULIP_RUN_POST_SETUP_SCRIPTS" != "true" ]; then
|
||||
echo "Not running post setup scripts. ZULIP_RUN_POST_SETUP_SCRIPTS isn't true."
|
||||
return 0
|
||||
fi
|
||||
if [ ! -d "$DATA_DIR/post-setup.d/" ]; then
|
||||
echo "No post-setup.d folder found. Continuing."
|
||||
return 0
|
||||
fi
|
||||
if [ ! "$(ls "$DATA_DIR/post-setup.d/")" ]; then
|
||||
echo "No post setup scripts found in \"$DATA_DIR/post-setup.d/\"."
|
||||
return 0
|
||||
fi
|
||||
set +e
|
||||
for file in "$DATA_DIR"/post-setup.d/*; do
|
||||
if [ -x "$file" ]; then
|
||||
echo "Executing \"$file\" ..."
|
||||
bash -c "$file"
|
||||
echo "Executed \"$file\". Return code $?."
|
||||
else
|
||||
echo "Permissions denied for \"$file\". Please check the permissions. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
set -e
|
||||
echo "Post setup scripts execution succeeded."
|
||||
}
|
||||
function runCertbotAsNeeded() {
|
||||
if [ ! "$GENERATE_CERTBOT_CERT_SCHEDULED" = "True" ]; then
|
||||
echo "Certbot is not scheduled to run."
|
||||
return
|
||||
fi
|
||||
|
||||
echo "Waiting for nginx to come online before generating certbot certificate ..."
|
||||
while ! curl -sk "$SETTING_EXTERNAL_HOST" >/dev/null 2>&1; do
|
||||
sleep 1;
|
||||
done
|
||||
|
||||
echo "Generating LetsEncrypt/certbot certificate ..."
|
||||
|
||||
# Remove the self-signed certs which were only needed to get Zulip going.
|
||||
rm -f "$DATA_DIR"/certs/zulip.key "$DATA_DIR"/certs/zulip.combined-chain.crt
|
||||
|
||||
ln -sf /sbin/certbot-deploy-hook /etc/letsencrypt/renewal-hooks/deploy/docker-deploy-hook
|
||||
|
||||
# Accept the terms of service automatically.
|
||||
/home/zulip/deployments/current/scripts/setup/setup-certbot \
|
||||
--agree-tos \
|
||||
--email="$SETTING_ZULIP_ADMINISTRATOR" \
|
||||
--skip-symlink \
|
||||
-- \
|
||||
"$SETTING_EXTERNAL_HOST"
|
||||
|
||||
echo "LetsEncrypt cert generated."
|
||||
}
|
||||
bootstrappingEnvironment() {
|
||||
echo "=== Begin Bootstrap Phase ==="
|
||||
waitingForDatabase
|
||||
zulipFirstStartInit
|
||||
zulipMigration
|
||||
runPostSetupScripts
|
||||
# Hack: We run this in the background, since we need nginx to be
|
||||
# started before we can create the certificate. See #142 for
|
||||
# details on how we can clean this up.
|
||||
runCertbotAsNeeded &
|
||||
echo "=== End Bootstrap Phase ==="
|
||||
}
|
||||
# END appRun functions
|
||||
# BEGIN app functions
|
||||
appRun() {
|
||||
initialConfiguration
|
||||
bootstrappingEnvironment
|
||||
echo "=== Begin Run Phase ==="
|
||||
echo "Starting Zulip using supervisor with \"/etc/supervisor/supervisord.conf\" config ..."
|
||||
echo ""
|
||||
exec supervisord -n -c "/etc/supervisor/supervisord.conf"
|
||||
}
|
||||
appInit() {
|
||||
echo "=== Running initial setup ==="
|
||||
initialConfiguration
|
||||
bootstrappingEnvironment
|
||||
}
|
||||
appManagePy() {
|
||||
COMMAND="$1"
|
||||
shift 1
|
||||
if [ -z "$COMMAND" ]; then
|
||||
echo "No command given for manage.py. Defaulting to \"shell\"."
|
||||
COMMAND="shell"
|
||||
fi
|
||||
echo "Running manage.py ..."
|
||||
set +e
|
||||
exec su zulip -c "/home/zulip/deployments/current/manage.py $(printf '%q ' "$COMMAND" "$@")"
|
||||
}
|
||||
appBackup() {
|
||||
echo "Starting backup process ..."
|
||||
local TIMESTAMP
|
||||
TIMESTAMP=$(date -u -Iseconds | tr ':' '_')
|
||||
if [ -d "/tmp/backup-$TIMESTAMP" ]; then
|
||||
echo "Temporary backup folder for \"$TIMESTAMP\" already exists. Aborting."
|
||||
echo "Backup process failed. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
local BACKUP_FOLDER
|
||||
BACKUP_FOLDER="/tmp/backup-$TIMESTAMP)"
|
||||
mkdir -p "$BACKUP_FOLDER"
|
||||
waitingForDatabase
|
||||
pg_dump -h "$DB_HOST" -p "$DB_HOST_PORT" -U "$DB_USER" "$DB_NAME" > "$BACKUP_FOLDER/database-postgres.sql"
|
||||
tar -zcvf "$DATA_DIR/backups/backup-$TIMESTAMP.tar.gz" "$BACKUP_FOLDER/"
|
||||
rm -r "${BACKUP_FOLDER:?}/"
|
||||
echo "Backup process succeeded."
|
||||
exit 0
|
||||
}
|
||||
appRestore() {
|
||||
echo "Starting restore process ..."
|
||||
if [ -z "$(ls -A "$DATA_DIR/backups/")" ]; then
|
||||
echo "No backups to restore found in \"$DATA_DIR/backups/\"."
|
||||
echo "Restore process failed. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
while true; do
|
||||
local backups=("$DATA_DIR"/backups/*)
|
||||
printf '|-> %s\n' "${backups[@]#"$DATA_DIR"/backups/}"
|
||||
echo "Please enter backup filename (full filename with extension): "
|
||||
read -r BACKUP_FILE
|
||||
if [ -z "$BACKUP_FILE" ]; then
|
||||
echo "Empty filename given. Please try again."
|
||||
echo ""
|
||||
continue
|
||||
fi
|
||||
if [ ! -e "$DATA_DIR/backups/$BACKUP_FILE" ]; then
|
||||
echo "File \"$BACKUP_FILE\" not found. Please try again."
|
||||
echo ""
|
||||
fi
|
||||
break
|
||||
done
|
||||
echo "File \"$BACKUP_FILE\" found."
|
||||
echo ""
|
||||
echo "==============================================================="
|
||||
echo "!! WARNING !! Your current data will be deleted!"
|
||||
echo "!! WARNING !! YOU HAVE BEEN WARNED! You can abort with \"CTRL+C\"."
|
||||
echo "!! WARNING !! Waiting 10 seconds before continuing ..."
|
||||
echo "==============================================================="
|
||||
echo ""
|
||||
local TIMEOUT
|
||||
for TIMEOUT in {10..1}; do
|
||||
echo "$TIMEOUT"
|
||||
sleep 1
|
||||
done
|
||||
echo "!! WARNING !! Starting restore process ... !! WARNING !!"
|
||||
waitingForDatabase
|
||||
tar -zxvf "$DATA_DIR/backups/$BACKUP_FILE" -C /tmp
|
||||
psql -h "$DB_HOST" -p "$DB_HOST_PORT" -U "$DB_USER" "$DB_NAME" < "/tmp/$(basename "$BACKUP_FILE" | cut -d. -f1)/database-postgres.sql"
|
||||
rm -r "/tmp/$(basename "$BACKUP_FILE" | cut -d. -f1)/"
|
||||
echo "Restore process succeeded. Exiting."
|
||||
exit 0
|
||||
}
|
||||
appCerts() {
|
||||
configureCerts
|
||||
}
|
||||
appHelp() {
|
||||
echo "Available commands:"
|
||||
echo "> app:help - Show this help menu and exit"
|
||||
echo "> app:version - Container Zulip server version"
|
||||
echo "> app:managepy - Run Zulip's manage.py script (defaults to \"shell\")"
|
||||
echo "> app:backup - Create backups of Zulip instances"
|
||||
echo "> app:restore - Restore backups of Zulip instances"
|
||||
echo "> app:certs - Create self-signed certificates"
|
||||
echo "> app:run - Run the Zulip server"
|
||||
echo "> app:init - Run inital setup of Zulip server"
|
||||
echo "> [COMMAND] - Run given command with arguments in shell"
|
||||
}
|
||||
appVersion() {
|
||||
echo "This container contains:"
|
||||
echo "> Zulip server $ZULIP_VERSION"
|
||||
echo "> Checksum: $ZULIP_CHECKSUM"
|
||||
exit 0
|
||||
}
|
||||
# END app functions
|
||||
|
||||
case "$1" in
|
||||
app:run)
|
||||
appRun
|
||||
;;
|
||||
app:init)
|
||||
appInit
|
||||
;;
|
||||
app:managepy)
|
||||
shift 1
|
||||
appManagePy "$@"
|
||||
;;
|
||||
app:backup)
|
||||
appBackup
|
||||
;;
|
||||
app:restore)
|
||||
appRestore
|
||||
;;
|
||||
app:certs)
|
||||
appCerts
|
||||
;;
|
||||
app:help)
|
||||
appHelp
|
||||
;;
|
||||
app:version)
|
||||
appVersion
|
||||
;;
|
||||
*)
|
||||
exec "$@" || appHelp
|
||||
;;
|
||||
esac
|
14
rabbitmq_healthcheck.sh
Normal file
14
rabbitmq_healthcheck.sh
Normal file
@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
set -eo pipefail
|
||||
|
||||
# A RabbitMQ node is considered healthy if all the below are true:
|
||||
# * the rabbit app finished booting & it's running
|
||||
# * there are no alarms
|
||||
# * there is at least 1 active listener
|
||||
|
||||
rabbitmqctl eval '
|
||||
{ true, rabbit_app_booted_and_running } = { rabbit:is_booted(node()), rabbit_app_booted_and_running },
|
||||
{ [], no_alarms } = { rabbit:alarms(), no_alarms },
|
||||
[] /= rabbit_networking:active_listeners(),
|
||||
rabbitmq_node_is_healthy.
|
||||
' || exit 1
|
Loading…
x
Reference in New Issue
Block a user