From 937238697922fec9e8ae8942d7a8b06292e3d869 Mon Sep 17 00:00:00 2001 From: Finn Stutzenstein Date: Mon, 5 Oct 2020 10:43:43 +0200 Subject: [PATCH] Prod setup --- .gitignore | 8 +- Makefile | 17 +- README.md | 19 + dc-dev.sh | 2 +- docker-compose.prod.yml | 100 --- docker-compose.yml | 71 -- docker/.env | 31 + docker/build.sh | 152 +++++ docker/docker-compose.dev.yml | 87 +++ docker/docker-compose.yml.m4 | 193 ++++++ docker/docker-stack.yml.m4 | 276 ++++++++ docker/initial-data.json | 627 ++++++++++++++++++ docker/reset-admin-password.sh | 14 + docker/secrets/admin.env.example | 3 + docker/secrets/user.env.example | 5 + docker/services.env | 23 + docker/setup-prod.sh | 11 + haproxy/Dockerfile | 5 +- haproxy/Dockerfile.dev | 4 +- haproxy/build.sh | 3 + .../src/{dev-haproxy.cfg => haproxy.dev.cfg} | 0 .../{prod-haproxy.cfg => haproxy.prod.cfg} | 0 openslides-autoupdate-service | 2 +- openslides-backend | 2 +- 24 files changed, 1461 insertions(+), 194 deletions(-) delete mode 100644 docker-compose.prod.yml delete mode 100644 docker-compose.yml create mode 100644 docker/.env create mode 100755 docker/build.sh create mode 100644 docker/docker-compose.dev.yml create mode 100644 docker/docker-compose.yml.m4 create mode 100644 docker/docker-stack.yml.m4 create mode 100644 docker/initial-data.json create mode 100755 docker/reset-admin-password.sh create mode 100644 docker/secrets/admin.env.example create mode 100644 docker/secrets/user.env.example create mode 100644 docker/services.env create mode 100755 docker/setup-prod.sh create mode 100755 haproxy/build.sh rename haproxy/src/{dev-haproxy.cfg => haproxy.dev.cfg} (100%) rename haproxy/src/{prod-haproxy.cfg => haproxy.prod.cfg} (100%) diff --git a/.gitignore b/.gitignore index 690249e9d..15f04f076 100644 --- a/.gitignore +++ b/.gitignore @@ -5,12 +5,15 @@ *~ .DS_Store .idea -.env *.code-workspace # certs *.pem +# Deployment +/docker/docker-compose.yml +/docker/docker-stack.yml + # Old OS3 files and folders .coverage .mypy_cache @@ -29,5 +32,4 @@ tests .vscode/ package-lock.json server/ -docker/ - +docker/keys \ No newline at end of file diff --git a/Makefile b/Makefile index 47cbf8bef..96b7f2470 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,3 @@ -SHELL := /bin/bash - run-system-tests: echo "TODO: write complete system tests" @@ -11,21 +9,14 @@ build-dev: make -C haproxy build-dev run-dev: | build-dev - docker-compose -f docker-compose.yml -f docker-compose.dev.yml up + docker-compose -f docker/docker-compose.dev.yml up stop-dev: - docker-compose -f docker-compose.yml -f docker-compose.dev.yml down + docker-compose -f docker/docker-compose.dev.yml down copy-node-modules: - docker-compose -f docker-compose.yml -f docker-compose.dev.yml exec client bash -c "cp -r /app/node_modules/ /app/src/" + docker-compose -f docker/docker-compose.dev.yml exec client bash -c "cp -r /app/node_modules/ /app/src/" mv openslides-client/client/src/node_modules/ openslides-client/client/ -build-prod: - git submodule status | awk '{ gsub(/[^0-9a-f]/, "", $$1); gsub("-","_",$$2); print toupper($$2)"_COMMIT_HASH="$$1 }' > .env - docker-compose -f docker-compose.yml -f docker-compose.prod.yml build - -run-prod: | build-prod - docker-compose -f docker-compose.yml -f docker-compose.prod.yml up - reload-haproxy: - docker-compose -f docker-compose.yml -f docker-compose.dev.yml kill -s HUP haproxy + docker-compose -f docker/docker-compose.dev.yml kill -s HUP haproxy diff --git a/README.md b/README.md index c7e2d3103..4f27e89c9 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,25 @@ For a non-development setup, clone this repo and run it via docker compose. The For a development setup, refer to [the development docs](DEVELOPMENT.md) +## Productive environment + +Setup the repository (may be already done) + + $ git clone git@github.com:OpenSlides/OpenSlides.git + $ cd OpenSlides + $ git checkout openslides4-dev + $ git submodule update --init + +Prod setup. `./build.sh` may take a while. + + $ cd docker + $ ./build.sh + $ ./setup-prod.sh + $ docker-compose up + +Navigate to https://localhost:8000 + + ## Used software OpenSlides uses the following projects or parts of them: diff --git a/dc-dev.sh b/dc-dev.sh index 9d59eee83..9cb85da85 100755 --- a/dc-dev.sh +++ b/dc-dev.sh @@ -1,3 +1,3 @@ #!/bin/bash cd "$(dirname $0)" -docker-compose -f docker-compose.yml -f docker-compose.dev.yml $@ \ No newline at end of file +docker-compose -f docker/docker-compose.dev.yml $@ diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml deleted file mode 100644 index 85de18f7f..000000000 --- a/docker-compose.prod.yml +++ /dev/null @@ -1,100 +0,0 @@ -version: "3" -services: - datastore-reader: - build: - context: "https://github.com/OpenSlides/openslides-datastore-service.git" - args: - GIT_CHECKOUT: "${OPENSLIDES_DATASTORE_SERVICE_COMMIT_HASH}" - MODULE: "reader" - PORT: "${OPENSLIDES_DATASTORE_READER_PORT}" - image: openslides-datastore-reader - networks: - - backend - - datastore-reader - - postgres - - datastore-writer: - build: - context: "https://github.com/OpenSlides/openslides-datastore-service.git" - args: - GIT_CHECKOUT: "${OPENSLIDES_DATASTORE_SERVICE_COMMIT_HASH}" - MODULE: "writer" - PORT: "${OPENSLIDES_DATASTORE_WRITER_PORT}" - image: openslides-datastore-writer - networks: - - backend - - postgres - - message-bus - - postgres: - networks: - - postgres - - client: - build: - context: "https://github.com/OpenSlides/openslides-client.git" - args: - GIT_CHECKOUT: "${OPENSLIDES_CLIENT_COMMIT_HASH}" - image: openslides-client - networks: - - frontend - - backend: - networks: - - frontend - - backend - - autoupdate: - networks: - - frontend - - backend - - message-bus - - auth: - build: - context: "https://github.com/OpenSlides/openslides-auth-service.git" - args: - GIT_CHECKOUT: "${OPENSLDIES_AUTH_SERVICE_COMMIT_HASH}" - PORT: "${OPENSLDIES_AUTH_SERVICE_PORT}" - image: openslides-auth - networks: - - datastore-reader - - auth - cache: - networks: - - auth - message-bus: - networks: - - message-bus - - media: - # TODO: build - image: openslides-media - networks: - - frontend - - backend - - postgres - - haproxy: - build: ./haproxy - image: openslides-haproxy - networks: - - uplink - - frontend - -# Setup: host <-uplink-> haproxy <-frontend-> services that are reachable from the client <-backend-> services that are internal-only -# There are special networks for some services only, e.g. postgres only for the postgresql, datastore reader and datastore writer -networks: - uplink: - frontend: - internal: true - backend: - internal: true - postgres: - internal: true - datastore-reader: - internal: true - message-bus: - internal: true - auth: - internal: true diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index b49fe4859..000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,71 +0,0 @@ -version: "3" -services: - # DATASTORE SECTION - datastore-reader: - depends_on: - - postgres - env_file: services.env - environment: - - NUM_WORKERS=8 - datastore-writer: - depends_on: - - postgres - - message-bus - env_file: services.env - - # CLIENT - client: - depends_on: - - backend - - autoupdate - - # BACKEND - backend: - depends_on: - - datastore-reader - - datastore-writer - env_file: services.env - - # AUTOUPDATE - autoupdate: - depends_on: - - datastore-reader - - message-bus - env_file: services.env - - # AUTH - auth: - depends_on: - - datastore-reader - - cache - env_file: services.env - cache: - image: redis:latest - - # MEDIA - media: - depends_on: - - backend - - postgres - env_file: services.env - - # PERSISTENCE - postgres: - image: postgres:11 - environment: - - POSTGRES_USER=openslides - - POSTGRES_PASSWORD=openslides - - POSTGRES_DB=openslides - - # SHARED - message-bus: - image: redis:latest - - # UPLINK - haproxy: - depends_on: - - client - - backend - - autoupdate - ports: - - "8000:8000" diff --git a/docker/.env b/docker/.env new file mode 100644 index 000000000..880256be8 --- /dev/null +++ b/docker/.env @@ -0,0 +1,31 @@ +# OpenSlides instance configuration +# +# As well as environment variables for various services, this file contains +# variables used to persist custom settings for docker-compose.yml or +# docker-stack.yml. See the preamble of a docker-compose.yml.m4 or +# docker-stack.yml.m4 template for more information. +# +# Most variables are listed here only to facilitate discovery of the available +# options. Empty values cause the template's defaults to be inserted. + +# General +# ------- +INSTANCE_DOMAIN= +PROJECT_STACK_NAME= +EXTERNAL_HTTP_PORT= +DEFAULT_DOCKER_REGISTRY= + +# Docker Images +# ------------- +DOCKER_OPENSLIDES_BACKEND_NAME= +DOCKER_OPENSLIDES_BACKEND_TAG= +DOCKER_OPENSLIDES_FRONTEND_NAME= +DOCKER_OPENSLIDES_FRONTEND_TAG= + +# Service Replication +# ------------------- +# TODO!! +OPENSLIDES_BACKEND_SERVICE_REPLICAS= +OPENSLIDES_FRONTEND_SERVICE_REPLICAS= +REDIS_RO_SERVICE_REPLICAS= +MEDIA_SERVICE_REPLICAS= diff --git a/docker/build.sh b/docker/build.sh new file mode 100755 index 000000000..f7fb9a789 --- /dev/null +++ b/docker/build.sh @@ -0,0 +1,152 @@ +#!/bin/bash + +set -e + +HOME=$(dirname "$(realpath "${BASH_SOURCE[0]}")") +declare -A TARGETS +TARGETS=( + [haproxy]="$HOME/../haproxy/" + [client]="$HOME/../openslides-client/" + [backend]="$HOME/../openslides-backend/" + [auth]="$HOME/../openslides-auth-service/" + [autoupdate]="$HOME/../openslides-autoupdate-service/" + [datastore-reader]="$HOME/../openslides-datastore-service/reader" + [datastore-writer]="$HOME/../openslides-datastore-service/writer" + [media]="$HOME/../openslides-media-service/" + #[pgbouncer]="https://github.com/OpenSlides/openslides-docker-compose.git#:pgbouncer" + #[postfix]="https://github.com/OpenSlides/openslides-docker-compose.git#:postfix" + #[repmgr]="https://github.com/OpenSlides/openslides-docker-compose.git#:repmgr" +) + +DOCKER_REPOSITORY="openslides" +DOCKER_TAG="latest" +CONFIG="/etc/osinstancectl" +OPTIONS=() +BUILT_IMAGES=() +DEFAULT_TARGETS=(haproxy client backend auth autoupdate datastore-reader datastore-writer media) + +usage() { + cat << EOF +Usage: $(basename ${BASH_SOURCE[0]}) [] ... + +Options: + -D, --docker-repo Specify a Docker repository + (default: unspecified, i.e., system default) + -t, --tag Tag the Docker image (default: $DOCKER_TAG) + --ask-push Offer to push newly built images to registry + --no-cache Pass --no-cache to docker-build +EOF +} + +# Config file +if [[ -f "$CONFIG" ]]; then + echo "Found ${CONFIG} file." + source "$CONFIG" +fi + +shortopt="hr:D:t:" +longopt="help,docker-repo:,tag:,ask-push,no-cache" +ARGS=$(getopt -o "$shortopt" -l "$longopt" -n "$ME" -- "$@") +if [ $? -ne 0 ]; then usage; exit 1; fi +eval set -- "$ARGS"; +unset ARGS + +# Parse options +while true; do + case "$1" in + -D|--docker-repo) + DOCKER_REPOSITORY="$2" + shift 2 + ;; + -t|--tag) + DOCKER_TAG="$2" + shift 2 + ;; + --ask-push) + ASK_PUSH=1 + shift 1 + ;; + --no-cache) + OPTIONS+="--no-cache" + shift 1 + ;; + -h|--help) usage; exit 0 ;; + --) shift ; break ;; + *) usage; exit 1 ;; + esac +done + +SELECTED_TARGETS=($@) +[[ "${#SELECTED_TARGETS[@]}" -ge 1 ]] || SELECTED_TARGETS=("${DEFAULT_TARGETS[@]}") +[[ "${SELECTED_TARGETS[@]}" != "all" ]] || SELECTED_TARGETS=("${!TARGETS[@]}") + +for i in "${SELECTED_TARGETS[@]}"; do + + loc="${TARGETS[$i]}" + [[ -n "$loc" ]] || { + echo "ERROR: Cannot build ${i}: not configured." + continue + } + + img_name="openslides-${i}" + img="${img_name}:${DOCKER_TAG}" + if [[ -n "$DOCKER_REPOSITORY" ]]; then + img="${DOCKER_REPOSITORY}/${img}" + fi + + echo "Building $img..." + cd $loc + { + printf '{\n' + printf '\t"service": "%s,\n' "${i}" + printf '\t"date": "%s",\n' "$(date)" + printf '\t"commit": "%s",\n' "$(git rev-parse HEAD)" + printf '\t"commit-abbrev": "%s",\n' "$(git rev-parse --abbrev-ref HEAD)" + printf '}\n' + } > version.json + + # Special instructions for local services + build_script="${loc}/build.sh" + if [[ -f "$build_script" ]]; then + ( . "$build_script" ) + else + docker build --tag "$img" --pull "${OPTIONS[@]}" "$loc" + fi + rm version.json + + BUILT_IMAGES+=("$img ON") +done + +if [[ "${#BUILT_IMAGES[@]}" -ge 1 ]]; then + printf "\nSuccessfully built images:\n\n" + for i in "${BUILT_IMAGES[@]}"; do + read -r img x <<< "$i" + printf " - $img\n" + done +else + echo "No images were built." + exit 3 +fi + +[[ "$ASK_PUSH" ]] || exit 0 + +if hash whiptail > /dev/null 2>&1; then + while read img; do + echo "Pushing ${img}." + docker push "$img" + done < <( whiptail --title "OpenSlides build script" \ + --checklist "Select images to push to their registry." \ + 25 78 16 --separate-output --noitem --clear \ + ${BUILT_IMAGES[@]} \ + 3>&2 2>&1 1>&3 ) +else + echo + for i in "${BUILT_IMAGES[@]}"; do + read -r img x <<< "$i" + read -p "Push image '$img' to repository? [Y/n] " REPL + case "$REPL" in + N|n|No|no|NO) exit 0;; + *) docker push "$img" ;; + esac + done +fi diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml new file mode 100644 index 000000000..b3b7ef861 --- /dev/null +++ b/docker/docker-compose.dev.yml @@ -0,0 +1,87 @@ +version: "3" +services: + datastore-reader: + image: openslides-datastore-reader-dev + depends_on: + - postgres + env_file: services.env + environment: + - NUM_WORKERS=8 + volumes: + - ../openslides-datastore-service/shared/shared:/app/shared + - ../openslides-datastore-service/reader/reader:/app/reader + datastore-writer: + image: openslides-datastore-writer-dev + depends_on: + - postgres + - message-bus + env_file: services.env + volumes: + - ../openslides-datastore-service/shared/shared:/app/shared + - ../openslides-datastore-service/writer/writer:/app/writer + - ../openslides-datastore-service/cli:/app/cli + environment: + - DATASTORE_ENABLE_DEV_ENVIRONMENT=1 + - COMMAND=create_initial_data + - DATASTORE_INITIAL_DATA_FILE=https://raw.githubusercontent.com/OpenSlides/OpenSlides/openslides4-dev/docs/example-data.json + postgres: + image: postgres:11 + environment: + - POSTGRES_USER=openslides + - POSTGRES_PASSWORD=openslides + - POSTGRES_DB=openslides + client: + image: openslides-client-dev + depends_on: + - backend + - autoupdate + env_file: services.env + volumes: + - ../openslides-client/client/src:/app/src + backend: + image: openslides-backend-dev + depends_on: + - datastore-reader + - datastore-writer + env_file: services.env + volumes: + - ../openslides-backend/openslides_backend:/srv/code/openslides_backend + autoupdate: + image: openslides-autoupdate-dev + depends_on: + - datastore-reader + - message-bus + env_file: services.env + volumes: + - ../openslides-autoupdate-service/cmd:/root/cmd + - ../openslides-autoupdate-service/internal:/root/internal + auth: + image: openslides-auth-dev + depends_on: + - datastore-reader + - cache + env_file: services.env + volumes: + - ../openslides-auth-service/auth/src:/app/src + cache: + image: redis:latest + media: + image: openslides-media-dev + depends_on: + - backend + - postgres + env_file: services.env + volumes: + - ../openslides-media-service/src:/app/src + message-bus: + image: redis:latest + haproxy: + image: openslides-haproxy-dev + depends_on: + - client + - backend + - autoupdate + ports: + - "8000:8000" + volumes: + - ../haproxy/src:/usr/local/etc/haproxy diff --git a/docker/docker-compose.yml.m4 b/docker/docker-compose.yml.m4 new file mode 100644 index 000000000..6ac675364 --- /dev/null +++ b/docker/docker-compose.yml.m4 @@ -0,0 +1,193 @@ +dnl This is a YAML template file. Simply translate it with m4 to create +dnl a standard configuration. Customizations can and should be added in .env +dnl by setting the appropriate variables. +dnl +dnl Usage: +dnl m4 docker-compose.yml.m4 > docker-compose.yml +dnl ( set -a; source .env; m4 docker-compose.yml.m4 ) > docker-compose.yml +dnl +dnl ---------------------------------------- +divert(-1)dnl +define(`read_env', `esyscmd(`printf "%s" "$$1"')') +define(`ifenvelse', `ifelse(read_env(`$1'),, `$2', read_env(`$1'))') + +define(`BACKEND_IMAGE', +ifenvelse(`DEFAULT_DOCKER_REGISTRY', openslides)/dnl +ifenvelse(`DOCKER_OPENSLIDES_BACKEND_NAME', openslides-backend):dnl +ifenvelse(`DOCKER_OPENSLIDES_BACKEND_TAG', latest)) +define(`HAPROXY_IMAGE', +ifenvelse(`DEFAULT_DOCKER_REGISTRY', openslides)/dnl +ifenvelse(`DOCKER_OPENSLIDES_HAPROXY_NAME', openslides-haproxy):dnl +ifenvelse(`DOCKER_OPENSLIDES_HAPROXY_TAG', latest)) +define(`CLIENT_IMAGE', +ifenvelse(`DEFAULT_DOCKER_REGISTRY', openslides)/dnl +ifenvelse(`DOCKER_OPENSLIDES_CLIENT_NAME', openslides-client):dnl +ifenvelse(`DOCKER_OPENSLIDES_CLIENT_TAG', latest)) +define(`AUTH_IMAGE', +ifenvelse(`DEFAULT_DOCKER_REGISTRY', openslides)/dnl +ifenvelse(`DOCKER_OPENSLIDES_AUTH_NAME', openslides-auth):dnl +ifenvelse(`DOCKER_OPENSLIDES_AUTH_TAG', latest)) +define(`AUTOUPDATE_IMAGE', +ifenvelse(`DEFAULT_DOCKER_REGISTRY', openslides)/dnl +ifenvelse(`DOCKER_OPENSLIDES_AUTOUPDATE_NAME', openslides-autoupdate):dnl +ifenvelse(`DOCKER_OPENSLIDES_AUTOUPDATE_TAG', latest)) +define(`DATASTORE_READER_IMAGE', +ifenvelse(`DEFAULT_DOCKER_REGISTRY', openslides)/dnl +ifenvelse(`DOCKER_OPENSLIDES_DATASTORE_READER_NAME', openslides-datastore-reader):dnl +ifenvelse(`DOCKER_OPENSLIDES_DATASTORE_READER_TAG', latest)) +define(`DATASTORE_WRITER_IMAGE', +ifenvelse(`DEFAULT_DOCKER_REGISTRY', openslides)/dnl +ifenvelse(`DOCKER_OPENSLIDES_DATASTORE_WRITER_NAME', openslides-datastore-writer):dnl +ifenvelse(`DOCKER_OPENSLIDES_DATASTORE_WRITER_TAG', latest)) +define(`MEDIA_IMAGE', +ifenvelse(`DEFAULT_DOCKER_REGISTRY', openslides)/dnl +ifenvelse(`DOCKER_OPENSLIDES_MEDIA_NAME', openslides-media):dnl +ifenvelse(`DOCKER_OPENSLIDES_MEDIA_TAG', latest)) + +define(`PROJECT_DIR', ifdef(`PROJECT_DIR',PROJECT_DIR,.)) +define(`ADMIN_SECRET_AVAILABLE', `syscmd(`test -f 'PROJECT_DIR`/secrets/admin.env')sysval') +define(`USER_SECRET_AVAILABLE', `syscmd(`test -f 'PROJECT_DIR`/secrets/user.env')sysval') +divert(0)dnl +dnl ---------------------------------------- +# This configuration was created from a template file. Before making changes, +# please make sure that you do not have a process in place that would override +# your changes in the future. The accompanying .env file might be the correct +# place for customizations instead. +version: '3.4' + +services: + haproxy: + image: HAPROXY_IMAGE + depends_on: + - client + - backend + - autoupdate + - auth + - media + networks: + - uplink + - frontend + ports: + - "127.0.0.1:ifenvelse(`EXTERNAL_HTTP_PORT', 8000):8000" + + client: + image: CLIENT_IMAGE + networks: + - frontend + depends_on: + - backend + - autoupdate + + backend: + image: BACKEND_IMAGE + depends_on: + - datastore-reader + - datastore-writer + env_file: services.env + networks: + - frontend + - backend + + datastore-reader: + image: DATASTORE_READER_IMAGE + depends_on: + - postgres + env_file: services.env + environment: + - NUM_WORKERS=8 + networks: + - backend + - datastore-reader + - postgres + datastore-writer: + image: DATASTORE_WRITER_IMAGE + depends_on: + - postgres + - message-bus + env_file: services.env + networks: + - backend + - postgres + - message-bus + environment: + - COMMAND=create_initial_data + - DATASTORE_INITIAL_DATA_FILE=/data/initial-data.json + volumes: + - ./initial-data.json:/data/initial-data.json + postgres: + image: postgres:11 + environment: + - POSTGRES_USER=openslides + - POSTGRES_PASSWORD=openslides + - POSTGRES_DB=openslides + networks: + - postgres + + autoupdate: + image: AUTOUPDATE_IMAGE + depends_on: + - datastore-reader + - message-bus + env_file: services.env + networks: + - frontend + - backend + - message-bus + + auth: + image: AUTH_IMAGE + depends_on: + - datastore-reader + - message-bus + - cache + env_file: services.env + networks: + - datastore-reader + - frontend + - message-bus + - auth + volumes: + - ./keys:/keys + cache: + image: redis:latest + networks: + - auth + + message-bus: + image: redis:latest + networks: + - message-bus + + media: + image: MEDIA_IMAGE + depends_on: + - backend + - postgres + env_file: services.env + networks: + - frontend + - backend + - postgres + +# Setup: host <-uplink-> haproxy <-frontend-> services that are reachable from the client <-backend-> services that are internal-only +# There are special networks for some services only, e.g. postgres only for the postgresql, datastore reader and datastore writer +networks: + uplink: + frontend: + internal: true + backend: + internal: true + postgres: + internal: true + datastore-reader: + internal: true + message-bus: + internal: true + auth: + internal: true + +dnl secrets: +dnl ifelse(ADMIN_SECRET_AVAILABLE, 0,os_admin: +dnl file: ./secrets/admin.env) +dnl ifelse(USER_SECRET_AVAILABLE, 0,os_user: +dnl file: ./secrets/user.env) diff --git a/docker/docker-stack.yml.m4 b/docker/docker-stack.yml.m4 new file mode 100644 index 000000000..640843363 --- /dev/null +++ b/docker/docker-stack.yml.m4 @@ -0,0 +1,276 @@ +dnl This is a YAML template file. Simply translate it with m4 to create +dnl a standard configuration. Customizations can and should be added in .env +dnl by setting the appropriate variables. +dnl +dnl Usage: +dnl m4 docker-stack.yml.m4 > docker-stack.yml +dnl ( set -a; source .env; m4 docker-stack.yml.m4 ) > docker-stack.yml +dnl +dnl ---------------------------------------- +divert(-1)dnl +define(`read_env', `esyscmd(`printf "%s" "$$1"')') +define(`ifenvelse', `ifelse(read_env(`$1'),, `$2', read_env(`$1'))') + +define(`BACKEND_IMAGE', +ifenvelse(`DOCKER_OPENSLIDES_BACKEND_NAME', openslides/openslides-server):dnl +ifenvelse(`DOCKER_OPENSLIDES_BACKEND_TAG', latest)) +define(`FRONTEND_IMAGE', +ifenvelse(`DOCKER_OPENSLIDES_FRONTEND_NAME', openslides/openslides-client):dnl +ifenvelse(`DOCKER_OPENSLIDES_FRONTEND_TAG', latest)) + +define(`PRIMARY_DB', `ifenvelse(`PGNODE_REPMGR_PRIMARY', pgnode1)') + +define(`PGBOUNCER_NODELIST', +`ifelse(read_env(`PGNODE_2_ENABLED'), 1, `,pgnode2')`'dnl +ifelse(read_env(`PGNODE_3_ENABLED'), 1, `,pgnode3')') + +define(`PROJECT_DIR', ifdef(`PROJECT_DIR',PROJECT_DIR,.)) +define(`ADMIN_SECRET_AVAILABLE', `syscmd(`test -f 'PROJECT_DIR`/secrets/adminsecret.env')sysval') +define(`USER_SECRET_AVAILABLE', `syscmd(`test -f 'PROJECT_DIR`/secrets/usersecret.env')sysval') +divert(0)dnl +dnl ---------------------------------------- +# This configuration was created from a template file. Before making changes, +# please make sure that you do not have a process in place that would override +# your changes in the future. The accompanying .env file might be the correct +# place for customizations instead. +version: '3.4' + +x-osserver: + &default-osserver + image: BACKEND_IMAGE + networks: + - front + - back +x-osserver-env: &default-osserver-env + AMOUNT_REPLICAS: ifenvelse(`REDIS_RO_SERVICE_REPLICAS', 3) + AUTOUPDATE_DELAY: ifenvelse(`AUTOUPDATE_DELAY', 1) + CONNECTION_POOL_LIMIT: ifenvelse(`CONNECTION_POOL_LIMIT', 100) + DATABASE_HOST: "ifenvelse(`DATABASE_HOST', pgbouncer)" + DATABASE_PASSWORD: "ifenvelse(`DATABASE_PASSWORD', openslides)" + DATABASE_PORT: ifenvelse(`DATABASE_PORT', 5432) + DATABASE_USER: "ifenvelse(`DATABASE_USER', openslides)" + DEFAULT_FROM_EMAIL: "ifenvelse(`DEFAULT_FROM_EMAIL', noreply@example.com)" + DJANGO_LOG_LEVEL: "ifenvelse(`DJANGO_LOG_LEVEL', INFO)" + EMAIL_HOST: "ifenvelse(`EMAIL_HOST', postfix)" + EMAIL_HOST_PASSWORD: "ifenvelse(`EMAIL_HOST_PASSWORD',)" + EMAIL_HOST_USER: "ifenvelse(`EMAIL_HOST_USER',)" + EMAIL_PORT: ifenvelse(`EMAIL_PORT', 25) + ENABLE_ELECTRONIC_VOTING: "ifenvelse(`ENABLE_ELECTRONIC_VOTING', False)" + ENABLE_SAML: "ifenvelse(`ENABLE_SAML', False)" + INSTANCE_DOMAIN: "ifenvelse(`INSTANCE_DOMAIN', http://example.com:8000)" + JITSI_DOMAIN: "ifenvelse(`JITSI_DOMAIN',)" + JITSI_ROOM_PASSWORD: "ifenvelse(`JITSI_ROOM_PASSWORD',)" + JITSI_ROOM_NAME: "ifenvelse(`JITSI_ROOM_NAME',)" + OPENSLIDES_LOG_LEVEL: "ifenvelse(`OPENSLIDES_LOG_LEVEL', INFO)" + REDIS_CHANNLES_HOST: "ifenvelse(`REDIS_CHANNLES_HOST', redis-channels)" + REDIS_CHANNLES_PORT: ifenvelse(`REDIS_CHANNLES_PORT', 6379) + REDIS_HOST: "ifenvelse(`REDIS_HOST', redis)" + REDIS_PORT: ifenvelse(`REDIS_PORT', 6379) + REDIS_SLAVE_HOST: "ifenvelse(`REDIS_SLAVE_HOST', redis-slave)" + REDIS_SLAVE_PORT: ifenvelse(`REDIS_SLAVE_PORT', 6379) + REDIS_SLAVE_WAIT_TIMEOUT: ifenvelse(`REDIS_SLAVE_WAIT_TIMEOUT', 10000) + RESET_PASSWORD_VERBOSE_ERRORS: "ifenvelse(`RESET_PASSWORD_VERBOSE_ERRORS', False)" +x-pgnode: &default-pgnode + image: ifenvelse(`DEFAULT_DOCKER_REGISTRY', openslides)/openslides-repmgr:latest + networks: + - dbnet + labels: + org.openslides.role: "postgres" + deploy: + replicas: 1 +x-pgnode-env: &default-pgnode-env + REPMGR_RECONNECT_ATTEMPTS: 30 + REPMGR_RECONNECT_INTERVAL: 10 + REPMGR_WAL_ARCHIVE: "ifenvelse(`PGNODE_WAL_ARCHIVING', on)" + +services: + server: + << : *default-osserver + # Below is the default command. You can uncomment it to override the + # number of workers, for example: + # command: "gunicorn -w 8 --preload -b 0.0.0.0:8000 + # -k uvicorn.workers.UvicornWorker openslides.asgi:application" + # + # Uncomment the following line to use daphne instead of gunicorn: + # command: "daphne -b 0.0.0.0 -p 8000 openslides.asgi:application" + environment: + << : *default-osserver-env + secrets: + - django + ifelse(read_env(`ENABLE_SAML'), `True',- saml_cert + - saml_key + - saml_config) + deploy: + restart_policy: + condition: on-failure + delay: 5s + replicas: ifenvelse(`OPENSLIDES_BACKEND_SERVICE_REPLICAS', 1) + + server-setup: + << : *default-osserver + entrypoint: /usr/local/sbin/entrypoint-db-setup + environment: + << : *default-osserver-env + secrets: + - django + ifelse(ADMIN_SECRET_AVAILABLE, 0,- os_admin) + ifelse(USER_SECRET_AVAILABLE, 0,- os_user) + ifelse(read_env(`ENABLE_SAML'), `True',- saml_cert + - saml_key + - saml_config) + + client: + image: FRONTEND_IMAGE + networks: + - front + ports: + - "0.0.0.0:ifenvelse(`EXTERNAL_HTTP_PORT', 8000):80" + deploy: + replicas: ifenvelse(`OPENSLIDES_FRONTEND_SERVICE_REPLICAS', 1) + restart_policy: + condition: on-failure + delay: 5s + + pgnode1: + << : *default-pgnode + environment: + << : *default-pgnode-env + REPMGR_NODE_ID: 1 + REPMGR_PRIMARY: ifelse(PRIMARY_DB, pgnode1, `# This is the primary', PRIMARY_DB) + deploy: + placement: + constraints: ifenvelse(`PGNODE_1_PLACEMENT_CONSTR', [node.labels.openslides-db == dbnode1]) + volumes: + - "dbdata1:/var/lib/postgresql" +ifelse(read_env(`PGNODE_2_ENABLED'), 1, `' + pgnode2: + << : *default-pgnode + environment: + << : *default-pgnode-env + REPMGR_NODE_ID: 2 + REPMGR_PRIMARY: ifelse(PRIMARY_DB, pgnode2, `# This is the primary', PRIMARY_DB) + deploy: + placement: + constraints: ifenvelse(`PGNODE_2_PLACEMENT_CONSTR', [node.labels.openslides-db == dbnode2]) + volumes: + - "dbdata2:/var/lib/postgresql") +ifelse(read_env(`PGNODE_3_ENABLED'), 1, `' + pgnode3: + << : *default-pgnode + environment: + << : *default-pgnode-env + REPMGR_NODE_ID: 3 + REPMGR_PRIMARY: ifelse(PRIMARY_DB, pgnode3, `# This is the primary', PRIMARY_DB) + deploy: + placement: + constraints: ifenvelse(`PGNODE_3_PLACEMENT_CONSTR', [node.labels.openslides-db == dbnode3]) + volumes: + - "dbdata3:/var/lib/postgresql") + + pgbouncer: + environment: + - PG_NODE_LIST=pgnode1`'PGBOUNCER_NODELIST + image: ifenvelse(`DEFAULT_DOCKER_REGISTRY', openslides)/openslides-pgbouncer:latest + networks: + back: + aliases: + - db + - postgres + dbnet: + deploy: + restart_policy: + condition: on-failure + delay: 10s + placement: + constraints: ifenvelse(`PGBOUNCER_PLACEMENT_CONSTR', [node.role == manager]) + postfix: + image: ifenvelse(`DEFAULT_DOCKER_REGISTRY', openslides)/openslides-postfix:latest + environment: + MYHOSTNAME: "ifenvelse(`POSTFIX_MYHOSTNAME', localhost)" + RELAYHOST: "ifenvelse(`POSTFIX_RELAYHOST', localhost)" + networks: + - back + deploy: + restart_policy: + condition: on-failure + delay: 5s + replicas: 1 + placement: + constraints: [node.role == manager] + redis: + image: redis:alpine + networks: + back: + aliases: + - rediscache + deploy: + replicas: 1 + restart_policy: + condition: on-failure + delay: 5s + redis-slave: + image: redis:alpine + command: ["redis-server", "--save", "", "--slaveof", "redis", "6379"] + networks: + back: + aliases: + - rediscache-slave + deploy: + replicas: ifenvelse(`REDIS_RO_SERVICE_REPLICAS', 3) + restart_policy: + condition: on-failure + delay: 5s + redis-channels: + image: redis:alpine + networks: + back: + deploy: + replicas: 1 + restart_policy: + condition: on-failure + delay: 5s + media: + image: ifenvelse(`DEFAULT_DOCKER_REGISTRY', openslides)/openslides-media-service:latest + environment: + - CHECK_REQUEST_URL=server:8000/check-media/ + deploy: + replicas: ifenvelse(`MEDIA_SERVICE_REPLICAS', 8) + restart_policy: + condition: on-failure + delay: 10s + networks: + front: + back: + # Override command to run more workers per task + # command: ["gunicorn", "-w", "4", "--preload", "-b", + # "0.0.0.0:8000", "src.mediaserver:app"] + +volumes: + dbdata1: +ifelse(read_env(`PGNODE_2_ENABLED'), 1, ` dbdata2:') +ifelse(read_env(`PGNODE_3_ENABLED'), 1, ` dbdata3:') + +networks: + front: + back: + driver_opts: + encrypted: "" + dbnet: + driver_opts: + encrypted: "" + +secrets: + django: + file: ./secrets/django.env + ifelse(ADMIN_SECRET_AVAILABLE, 0,os_admin: + file: ./secrets/adminsecret.env) + ifelse(USER_SECRET_AVAILABLE, 0,os_user: + file: ./secrets/usersecret.env) + ifelse(read_env(`ENABLE_SAML'), `True', saml_cert: + file: ./secrets/saml/sp.crt + saml_key: + file: ./secrets/saml/sp.key + saml_config: + file: ./secrets/saml/saml_settings.json) + +# vim: set sw=2 et: diff --git a/docker/initial-data.json b/docker/initial-data.json new file mode 100644 index 000000000..33eca5882 --- /dev/null +++ b/docker/initial-data.json @@ -0,0 +1,627 @@ +{ +"organisation": [ + { + "id": 1, + "legal_notice": "OpenSlides is a free web based presentation and assembly system for visualizing and controlling agenda, motions and elections of an assembly.", + "openslides_theme": "openslides-theme", + + "committee_ids": [1], + "role_ids": [1], + "superadmin_role_id": 1, + "resource_ids": [] + }], +"user": [ + { + "id": 1, + "username": "admin", + "title": "", + "first_name": "", + "last_name": "Administrator", + "is_active": true, + "is_committee": false, + "password": "1422e767c5e08bb7196844025a0f98e1x61Ey612Kl2gpFL56FT9weDnpSo4AV8j8+qx2AuTHdRyY036xxzTTrw10Wq3+4qQyB+XURPWx1ONxp3Y3pB37A==", + "default_password": "admin", + "about_me": "", + "gender": "", + "comment": "", + "number": "", + "structure_level": "", + "email": "", + "last_email_send": null, + "vote_weight": "1.000000", + + "role_id": 1, + + "is_present_in_meeting_ids": [], + "meeting_id": null, + "guest_meeting_ids": [], + "committee_as_member_ids": [], + "committee_as_manager_ids": [], + + "projection_ids": [], + "current_projector_ids": [], + + "group_$_ids": ["1"], + "group_1_ids": [2], + "speaker_$_ids": [], + "personal_note_$_ids": [], + "supported_motion_$_ids": [], + "submitted_motion_$_ids": [], + "motion_poll_voted_$_ids": [], + "motion_vote_$_ids": [], + "assignment_candidate_$_ids": [], + "assignment_poll_voted_$_ids": [], + "assignment_option_$_ids": [], + "assignment_vote_$_ids": [] + }], +"role": [ + { + "id": 1, + "name": "Superadmin role", + "permissions": [], + + "organisation_id": 1, + "superadmin_role_for_organisation_id": 1, + "user_ids": [1] + }], +"resource": [], +"committee": [ + { + "id": 1, + "name": "Default committee", + "description": "Add description here", + + "meeting_ids": [1], + "template_meeting_id": null, + "default_meeting_id": 1, + "member_ids": [], + "manager_ids": [], + "forward_to_committee_ids": [], + "receive_forwardings_from_committee_ids": [], + "organisation_id": 1 + }], +"meeting": [ + { + "id": 1, + "url_name": "os3_test", + "template_for_committee_id": null, + "enable_anonymous": false, + + "name": "OpenSlides - Die Veranstaltung (Teil II)", + "description": "Presentation and assembly system", + "location": "", + "start_time": 0, + "end_time": 0, + "welcome_title": "Welcome to OpenSlides", + "welcome_text": "[Space for your welcome text.]", + "custom_translations": [], + + "conference_show": false, + "conference_auto_connect": false, + "conference_los_restriction": false, + "conference_stream_url": "", + + "projector_default_countdown_time": 60, + "projector_countdown_warning_time": 0, + + "export_csv_encoding": "utf-8", + "export_csv_separator": ",", + "export_pdf_pagenumber_alignment": "center", + "export_pdf_fontsize": "10", + "export_pdf_pagesize": "A4", + + "agenda_show_subtitles": false, + "agenda_enable_numbering": true, + "agenda_number_prefix": "", + "agenda_numeral_system": "arabic", + "agenda_item_creation": "default_yes", + "agenda_new_items_default_visibility": "2", + "agenda_show_internal_items_on_projector": false, + + "list_of_speakers_amount_last_on_projector": 1, + "list_of_speakers_amount_next_on_projector": -1, + "list_of_speakers_couple_countdown": false, + "list_of_speakers_show_amount_of_speakers_on_slide": true, + "list_of_speakers_present_users_only": false, + "list_of_speakers_show_first_contribution": false, + + "motions_default_workflow_id": 1, + "motions_statute_amendment_workflow_id": 1, + "motions_preamble": "The assembly may decide:", + "motions_default_line_numbering": "none", + "motions_line_length": 90, + "motions_reason_required": false, + "motions_enable_text_on_projector": true, + "motions_enable_reason_on_projector": true, + "motions_enable_sidebox_on_projector": false, + "motions_enable_recommendation_on_projector": true, + "motions_show_referring_motions": true, + "motions_show_sequential_numbers": true, + "motions_recommendations_by": "", + "motions_statute_recommendations_by": "", + "motions_recommendation_text_mode": "original", + "motions_default_sorting": "number", + "motions_number_type": "per_category", + "motions_number_min_digits": 1, + "motions_number_with_blank": false, + "motions_statutes_enabled": true, + "motions_amendments_enabled": true, + "motions_amendments_in_main_list": true, + "motions_amendments_of_amendments": false, + "motions_amendments_prefix": "-", + "motions_amendments_text_mode": "freestyle", + "motions_amendments_multiple_paragraphs": true, + "motions_supporters_min_amount": 1, + "motions_supporters_autoremove": false, + "motions_export_title": "Motions", + "motions_export_preamble": "", + "motions_export_submitter_recommendation": false, + "motions_export_follow_recommendation": false, + + "motion_poll_ballot_paper_selection": "CUSTOM_NUMBER", + "motion_poll_ballot_paper_number": 8, + "motion_poll_default_type": "analog", + "motion_poll_default_100_percent_base": "YNA", + "motion_poll_default_majority_method": "simple", + "motion_poll_default_group_ids": [2, 3], + + "users_sort_by": "first_name", + "users_enable_presence_view": true, + "users_enable_vote_weight": true, + "users_allow_self_set_present": true, + "users_pdf_welcometitle": "Welcome to OpenSlides", + "users_pdf_welcometext": "[Place for your welcome and help text.]", + "users_pdf_url": "http://example.com:8000", + "users_pdf_wlan_ssid": "", + "users_pdf_wlan_password": "", + "users_pdf_wlan_encryption": "", + "users_email_sender": "noreply@yourdomain.com", + "users_email_replyto": "", + "users_email_subject": "Your login for {event_name}", + "users_email_body": "Dear {name},\n\nthis is your OpenSlides login for the event {event_name}:\n\n {url}\n username: {username}\n password: {password}\n\nThis email was generated automatically.", + + "assignments_export_title": "Elections", + "assignments_export_preamble": "", + "assignment_poll_add_candidates_to_list_of_speakers": true, + "assignment_poll_sort_poll_result_by_votes": true, + "assignment_poll_default_type": "nominal", + "assignment_poll_default_method": "votes", + "assignment_poll_default_100_percent_base": "valid", + "assignment_poll_default_majority_method": "simple", + "assignment_poll_default_group_ids": [3, 5], + + "projector_ids": [1], + "projectiondefault_ids": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], + "projector_message_ids": [], + "projector_countdown_ids": [], + "tag_ids": [], + "agenda_item_ids": [], + "list_of_speakers_ids": [], + "topic_ids": [], + "group_ids": [1, 2, 3, 5, 6], + "mediafile_ids": [], + "motion_ids": [], + "motion_comment_section_ids": [], + "motion_category_ids": [], + "motion_block_ids": [], + "motion_workflow_ids": [1], + "motion_statute_paragraph_ids": [], + "motion_poll_ids": [], + "assignment_ids": [], + "assignment_poll_ids": [], + + "logo_$_id": [], + "font_$_id": [], + + "committee_id": 1, + "default_meeting_for_committee_id": 1, + "present_user_ids": [], + "temorary_user_ids": [], + "guest_ids": [], + "user_ids": [], + "reference_projector_id": 2, + + "default_group_id": 1, + "superadmin_group_id": 2 + }], +"group": [ + { + "id": 1, + "name": "Default", + "superadmin_group_for_meeting_id": null, + "default_group_for_meeting_id": 1, + "permissions": [ + "agenda.can_see", + "agenda.can_see_internal_items", + "assignments.can_see", + "core.can_see_frontpage", + "core.can_see_projector", + "mediafiles.can_see", + "motions.can_see", + "users.can_see_name" + ], + + "user_ids": [], + "mediafile_access_group_ids": [], + "read_comment_section_ids": [], + "write_comment_section_ids": [], + "motion_poll_ids": [], + "assignment_poll_ids": [], + "used_as_motion_poll_default_id": null, + "used_as_assignment_poll_default_id": null, + "meeting_id": 1 + }, + { + "id": 2, + "name": "Admin", + "superadmin_group_for_meeting_id": 1, + "default_group_for_meeting_id": null, + "permissions": [], + + "user_ids": [], + "mediafile_access_group_ids": [], + "mediafile_inherited_access_group_ids": [], + "read_comment_section_ids": [], + "write_comment_section_ids": [], + "motion_poll_ids": [], + "assignment_poll_ids": [], + "used_as_motion_poll_default_id": 1, + "used_as_assignment_poll_default_id": null, + "meeting_id": 1 + }, + { + "id": 3, + "name": "Staff", + "superadmin_group_for_meeting_id": null, + "default_group_for_meeting_id": null, + "permissions": [ + "agenda.can_manage", + "agenda.can_see", + "agenda.can_see_internal_items", + "agenda.can_be_speaker", + "assignments.can_manage", + "assignments.can_nominate_other", + "assignments.can_nominate_self", + "assignments.can_see", + "core.can_manage_projector", + "core.can_see_frontpage", + "core.can_see_projector", + "core.can_manage_tags", + "mediafiles.can_manage", + "mediafiles.can_see", + "motions.can_create", + "motions.can_manage", + "motions.can_see", + "users.can_manage", + "users.can_see_extra_data", + "users.can_see_name" + ], + + "user_ids": [], + "mediafile_access_group_ids": [], + "mediafile_inherited_access_group_ids": [], + "read_comment_section_ids": [], + "write_comment_section_ids": [], + "motion_poll_ids": [], + "assignment_poll_ids": [], + "used_as_motion_poll_default_id": 1, + "used_as_assignment_poll_default_id": 1, + "meeting_id": 1 + }, + { + "id": 5, + "name": "Committees", + "superadmin_group_for_meeting_id": null, + "default_group_for_meeting_id": null, + "permissions": [ + "agenda.can_see", + "agenda.can_see_internal_items", + "assignments.can_see", + "core.can_see_frontpage", + "core.can_see_projector", + "mediafiles.can_see", + "motions.can_create", + "motions.can_see", + "motions.can_support", + "users.can_see_name" + ], + + "user_ids": [], + "mediafile_access_group_ids": [], + "read_comment_section_ids": [], + "write_comment_section_ids": [], + "motion_poll_ids": [], + "assignment_poll_ids": [], + "used_as_motion_poll_default_id": null, + "used_as_assignment_poll_default_id": 1, + "meeting_id": 1 + }, + { + "id": 6, + "name": "Delegates", + "superadmin_group_for_meeting_id": null, + "default_group_for_meeting_id": null, + "permissions": [ + "agenda.can_see", + "agenda.can_see_internal_items", + "agenda.can_be_speaker", + "assignments.can_nominate_other", + "assignments.can_nominate_self", + "assignments.can_see", + "core.can_see_frontpage", + "core.can_see_projector", + "mediafiles.can_see", + "motions.can_create", + "motions.can_manage", + "motions.can_see", + "motions.can_support", + "users.can_see_name" + ], + + "user_ids": [], + "mediafile_access_group_ids": [], + "read_comment_section_ids": [], + "write_comment_section_ids": [], + "motion_poll_ids": [], + "assignment_poll_ids": [], + "used_as_motion_poll_default_id": null, + "used_as_assignment_poll_default_id": null, + "meeting_id": 1 + }], +"personal_note": [], +"tag": [], +"agenda_item": [], +"list_of_speakers": [], +"speaker": [], +"topic": [], +"motion": [], +"motion_submitter": [], +"motion_comment": [], +"motion_comment_section": [], +"motion_category": [], +"motion_block": [], +"motion_change_recommendation": [], +"motion_state": [ + { + "id": 1, + "name": "submitted", + "recommendation_label": null, + "css_class": "lightblue", + "restrictions": [], + "allow_support": true, + "allow_create_poll": true, + "allow_submitter_edit": true, + "set_number": true, + "show_state_extension_field": false, + "merge_amendment_into_final": 0, + "show_recommendation_extension_field": false, + + "next_state_ids": [2, 3, 4], + "previous_state_ids": [], + "motion_ids": [], + "motion_recommendation_ids": [], + "workflow_id": 1, + "first_state_of_workflow_id": 1 + }, + { + "id": 2, + "name": "accepted", + "recommendation_label": "Acceptance", + "css_class": "green", + "restrictions": [], + "allow_support": false, + "allow_create_poll": false, + "allow_submitter_edit": false, + "set_number": true, + "show_state_extension_field": false, + "merge_amendment_into_final": 0, + "show_recommendation_extension_field": false, + + "next_state_ids": [], + "previous_state_ids": [1], + "motion_ids": [], + "motion_recommendation_ids": [], + "workflow_id": 1, + "first_state_of_workflow_id": null + }, + { + "id": 3, + "name": "rejected", + "recommendation_label": "Rejection", + "css_class": "red", + "restrictions": [], + "allow_support": false, + "allow_create_poll": false, + "allow_submitter_edit": false, + "set_number": true, + "show_state_extension_field": false, + "merge_amendment_into_final": 0, + "show_recommendation_extension_field": false, + + "next_state_ids": [], + "previous_state_ids": [1], + "motion_ids": [], + "motion_recommendation_ids": [], + "workflow_id": 1, + "first_state_of_workflow_id": null + }, + { + "id": 4, + "name": "not decided", + "recommendation_label": "No decision", + "css_class": "grey", + "restrictions": [], + "allow_support": false, + "allow_create_poll": false, + "allow_submitter_edit": false, + "set_number": true, + "show_state_extension_field": false, + "merge_amendment_into_final": 0, + "show_recommendation_extension_field": false, + + "next_state_ids": [], + "previous_state_ids": [1], + "motion_ids": [], + "motion_recommendation_ids": [], + "workflow_id": 1, + "first_state_of_workflow_id": null + } + ], +"motion_workflow": [ + { + "id": 1, + "name": "Simple Workflow", + + "state_ids": [1, 2, 3, 4], + "first_state_id": 1, + "default_workflow_meeting_id": 1, + "default_statute_amendments_meeting_id": 1, + "meeting_id": 1 + }], +"motion_statute_paragraph": [], +"motion_poll": [], +"motion_option": [], +"motion_vote": [], +"assignment": [], +"assignment_candidate": [], +"assignment_poll": [], +"assignment_option": [], +"assignment_vote": [], +"mediafile": [], +"projector": [ + { + "id": 1, + "name": "Default projector", + "scale": 0, + "scroll": 0, + "width": 1220, + "aspect_ratio_numerator": 4, + "aspect_ratio_denominator": 3, + "color": "#000000", + "background_color": "#ffffff", + "header_background_color": "#317796", + "header_font_color": "#f5f5f5", + "header_h1_color": "#317796", + "chyron_background_color": "#317796", + "chyron_font_color": "#ffffff", + "show_header_footer": true, + "show_title": true, + "show_logo": true, + + "current_projection_ids": [], + "current_element_ids": [], + "preview_projection_ids": [], + "history_projection_ids": [], + "used_as_reference_projector_meeting_id": null, + "projectiondefault_ids": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], + "meeting_id": 1 + }], +"projection": [], +"projectiondefault": [ + { + "id": 1, + "name": "agenda_all_items", + "display_name": "Agenda", + + "projector_id": 1, + "meeting_id": 1 + }, + { + "id": 2, + "name": "topics", + "display_name": "Topics", + + "projector_id": 1, + "meeting_id": 1 + }, + { + "id": 3, + "name": "agenda_list_of_speakers", + "display_name": "List of speakers", + + "projector_id": 1, + "meeting_id": 1 + }, + { + "id": 4, + "name": "agenda_current_list_of_speakers", + "display_name": "Current list of speakers", + + "projector_id": 1, + "meeting_id": 1 + }, + { + "id": 5, + "name": "motions", + "display_name": "Motions", + + "projector_id": 1, + "meeting_id": 1 + }, + { + "id": 6, + "name": "motionBlocks", + "display_name": "Motion blocks", + + "projector_id": 1, + "meeting_id": 1 + }, + { + "id": 7, + "name": "assignments", + "display_name": "Elections", + + "projector_id": 1, + "meeting_id": 1 + }, + { + "id": 8, + "name": "users", + "display_name": "Participants", + + "projector_id": 1, + "meeting_id": 1 + }, + { + "id": 9, + "name": "mediafiles", + "display_name": "Files", + + "projector_id": 1, + "meeting_id": 1 + }, + { + "id": 10, + "name": "messages", + "display_name": "Messages", + + "projector_id": 1, + "meeting_id": 1 + }, + { + "id": 11, + "name": "countdowns", + "display_name": "Countdowns", + + "projector_id": 1, + "meeting_id": 1 + }, + { + "id": 12, + "name": "assignment_poll", + "display_name": "Assignment poll", + + "projector_id": 1, + "meeting_id": 1 + }, + { + "id": 13, + "name": "motion_poll", + "display_name": "Motion Poll", + + "projector_id": 1, + "meeting_id": 1 + }], +"projector_message": [], +"projector_countdown": [] +} diff --git a/docker/reset-admin-password.sh b/docker/reset-admin-password.sh new file mode 100755 index 000000000..870ada26a --- /dev/null +++ b/docker/reset-admin-password.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +set -e + +# hash the new password +response=$(docker-compose exec auth curl --header "Content-Type: application/json" -d '{"toHash": "admin"}' http://localhost:9004/internal/auth/hash) +hash=$(jq .hash <<< $response) + +# Set user/1/password to $hash +request_data_prefix='{"user_id": 1, "information": {}, "locked_fields": {}, "events": [{"type": "update", "fqid": "user/1", "fields": {"password":' +request_data="$request_data_prefix $hash}}]}" +docker-compose exec backend curl --header "Content-Type: application/json" -d "$request_data" http://datastore-writer:9011/internal/datastore/writer/write + +echo "Done" diff --git a/docker/secrets/admin.env.example b/docker/secrets/admin.env.example new file mode 100644 index 000000000..d2c92f4e2 --- /dev/null +++ b/docker/secrets/admin.env.example @@ -0,0 +1,3 @@ +## secrets/adminsecret.env is sourced by the server container to set the initial +## admin user password. +# OPENSLIDES_ADMIN_PASSWORD="" diff --git a/docker/secrets/user.env.example b/docker/secrets/user.env.example new file mode 100644 index 000000000..d4a334bc0 --- /dev/null +++ b/docker/secrets/user.env.example @@ -0,0 +1,5 @@ +## Example user credential configuration +# OPENSLIDES_USER_FIRSTNAME="John" +# OPENSLIDES_USER_LASTNAME="Doe" +# OPENSLIDES_USER_PASSWORD="" +# OPENSLIDES_USER_EMAIL="john@example.com" diff --git a/docker/services.env b/docker/services.env new file mode 100644 index 000000000..8444aa769 --- /dev/null +++ b/docker/services.env @@ -0,0 +1,23 @@ +MESSAGE_BUS_HOST=message-bus +MESSAGE_BUS_PORT=6379 + +DATASTORE_READER_HOST=datastore-reader +DATASTORE_READER_PORT=9010 +DATASTORE_WRITER_HOST=datastore-writer +DATASTORE_WRITER_PORT=9011 +DATASTORE_DATABASE_HOST=postgres + +ACTION_HOST=backend +ACTION_PORT=9002 +PRESENTER_HOST=backend +PRESENTER_PORT=9003 + +AUTH_HOST=auth +AUTH_PORT=9004 +CACHE_HOST=cache +CACHE_PORT=6379 + +MEDIA_HOST=media +MEDIA_PORT=9006 +MEDIA_DATABASE_HOST=postgres +MEDIA_DATABASE_NAME=openslides diff --git a/docker/setup-prod.sh b/docker/setup-prod.sh new file mode 100755 index 000000000..f20948b6d --- /dev/null +++ b/docker/setup-prod.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Create keys for auth, if they do not exist +if [ ! -d keys ]; then + mkdir keys + + ssh-keygen -f keys/rsa-token.key -t rsa -b 2048 -P "" + ssh-keygen -f keys/rsa-cookie.key -t rsa -b 2048 -P "" +fi + +( set -a; source .env; m4 docker-compose.yml.m4 ) > docker-compose.yml diff --git a/haproxy/Dockerfile b/haproxy/Dockerfile index 0984eabcb..ed57f2e90 100644 --- a/haproxy/Dockerfile +++ b/haproxy/Dockerfile @@ -1,4 +1,5 @@ FROM haproxy:2.0-alpine COPY src/haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg -COPY src/prod-haproxy.cfg /usr/local/etc/haproxy/prod-haproxy.cfg -CMD ["haproxy", "-f", "/usr/local/etc/haproxy/haproxy.cfg", "-f", "/usr/local/etc/haproxy/prod-haproxy.cfg"] +COPY src/haproxy.prod.cfg /usr/local/etc/haproxy/haproxy.prod.cfg +COPY src/combined.pem /usr/local/etc/haproxy/combined.pem +CMD ["haproxy", "-f", "/usr/local/etc/haproxy/haproxy.cfg", "-f", "/usr/local/etc/haproxy/haproxy.prod.cfg"] diff --git a/haproxy/Dockerfile.dev b/haproxy/Dockerfile.dev index f21ee206a..1b947f499 100644 --- a/haproxy/Dockerfile.dev +++ b/haproxy/Dockerfile.dev @@ -1,5 +1,5 @@ FROM haproxy:2.0-alpine COPY src/haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg -COPY src/dev-haproxy.cfg /usr/local/etc/haproxy/dev-haproxy.cfg +COPY src/haproxy.dev.cfg /usr/local/etc/haproxy/haproxy.dev.cfg COPY src/combined.pem /usr/local/etc/haproxy/combined.pem -CMD ["haproxy", "-f", "/usr/local/etc/haproxy/haproxy.cfg", "-f", "/usr/local/etc/haproxy/dev-haproxy.cfg"] +CMD ["haproxy", "-f", "/usr/local/etc/haproxy/haproxy.cfg", "-f", "/usr/local/etc/haproxy/haproxy.dev.cfg"] diff --git a/haproxy/build.sh b/haproxy/build.sh new file mode 100755 index 000000000..90daca5ff --- /dev/null +++ b/haproxy/build.sh @@ -0,0 +1,3 @@ +./prepare-cert.sh +docker build --tag "${img:-openslides/openslides-haproxy:latest}" \ + --pull "${OPTIONS[@]}" . \ No newline at end of file diff --git a/haproxy/src/dev-haproxy.cfg b/haproxy/src/haproxy.dev.cfg similarity index 100% rename from haproxy/src/dev-haproxy.cfg rename to haproxy/src/haproxy.dev.cfg diff --git a/haproxy/src/prod-haproxy.cfg b/haproxy/src/haproxy.prod.cfg similarity index 100% rename from haproxy/src/prod-haproxy.cfg rename to haproxy/src/haproxy.prod.cfg diff --git a/openslides-autoupdate-service b/openslides-autoupdate-service index e22160c0b..12af349d2 160000 --- a/openslides-autoupdate-service +++ b/openslides-autoupdate-service @@ -1 +1 @@ -Subproject commit e22160c0b953fbbe01eb1e79c72bdf0701d5e288 +Subproject commit 12af349d2d90246d3492939278d09872c2f40f44 diff --git a/openslides-backend b/openslides-backend index 32b809bc2..9938ed0e7 160000 --- a/openslides-backend +++ b/openslides-backend @@ -1 +1 @@ -Subproject commit 32b809bc2d6a046b267e01fb2dd641d9eff64093 +Subproject commit 9938ed0e73793f98aac2d2d5ef246f2301e85855