|
| 1 | +#!/usr/bin/env bash |
| 2 | +set -eux |
| 3 | + |
| 4 | +new_version=14 |
| 5 | + |
| 6 | +# Require the `yq` tool |
| 7 | +if ! command -v yq >/dev/null; then |
| 8 | + echo "You must install the 'yq' tool to use this script." |
| 9 | + exit 1 |
| 10 | +fi |
| 11 | + |
| 12 | +image=$(yq ".services.database.image" docker-compose.yml) |
| 13 | +if [[ $image =~ ^zulip/zulip-postgresql:([0-9]+)$ ]]; then |
| 14 | + old_version="${BASH_REMATCH[1]}" |
| 15 | +else |
| 16 | + echo "Unexpected PostgreSQL image: $image" |
| 17 | + exit 1 |
| 18 | +fi |
| 19 | + |
| 20 | +volume_mount=$(yq ".services.database.volumes.0" docker-compose.yml) |
| 21 | +if [[ "$volume_mount" =~ ^([^:]+):/var/lib/postgresql/data:rw$ ]]; then |
| 22 | + old_mountpoint="${BASH_REMATCH[1]}" |
| 23 | +else |
| 24 | + echo "Unexpected volume mount: $volume_mount" |
| 25 | + exit 1 |
| 26 | +fi |
| 27 | + |
| 28 | +if [ "$new_version" -eq "$old_version" ]; then |
| 29 | + echo "PostgreSQL image is already version $new_version!" |
| 30 | + exit 1 |
| 31 | +fi |
| 32 | + |
| 33 | +# Create a new volume for the data; scope it with the current |
| 34 | +# directory, like docker-compose |
| 35 | +docker_compose_project="$(basename "$(pwd)")" |
| 36 | +new_volume="postgresql-$new_version" |
| 37 | +full_new_volume="${docker_compose_project}_${new_volume}" |
| 38 | +docker volume create "$full_new_volume" |
| 39 | +trap 'docker volume --force "$full_new_volume"' EXIT |
| 40 | + |
| 41 | +# Start a new PostgreSQL container of the right version to read in the |
| 42 | +# dumped database and write a new data dir on the new volume |
| 43 | +temp_container=$( |
| 44 | + docker run -d \ |
| 45 | + -e POSTGRES_DB=zulip \ |
| 46 | + -e POSTGRES_USER=zulip \ |
| 47 | + -e POSTGRES_PASSWORD=zulip \ |
| 48 | + -v "$full_new_volume:/var/lib/postgresql/data:rw" \ |
| 49 | + --health-cmd 'psql -U zulip -c "select 1"' \ |
| 50 | + --health-interval 10s \ |
| 51 | + "zulip/zulip-postgresql:$new_version" |
| 52 | +) |
| 53 | +trap 'docker volume rm --force "$full_new_volume"; docker rm --force "$temp_container"' EXIT |
| 54 | + |
| 55 | +# Wait for the new PostgreSQL container to become available |
| 56 | +tries=0 |
| 57 | +while [ "$(docker inspect --format='{{json .State.Health.Status}}' "$temp_container")" != '"healthy"' ]; do |
| 58 | + tries=$((tries + 1)) |
| 59 | + if [ "$tries" -gt 5 ]; then |
| 60 | + echo "PostgreSQL $new_version container failed to start!" |
| 61 | + exit 1 |
| 62 | + fi |
| 63 | + sleep 10 |
| 64 | +done |
| 65 | + |
| 66 | +# Ensure database is running |
| 67 | +docker-compose up --wait database |
| 68 | + |
| 69 | +# Stop the zulip processes which talk to the database |
| 70 | +zulip_is_running=$(docker-compose ps --filter status=running --services | grep zulip || true) |
| 71 | +if [ -n "$zulip_is_running" ]; then |
| 72 | + docker-compose stop zulip |
| 73 | +fi |
| 74 | + |
| 75 | +# Transfer the data to the new container |
| 76 | +docker-compose exec database pg_dumpall -U zulip | |
| 77 | + docker exec -i "$temp_container" psql -U zulip |
| 78 | + |
| 79 | +if [ "$old_version" -eq "10" ]; then |
| 80 | + # Upgrade MD5 password to SCRAM-SHA-256. We escape all 's by doubling them. |
| 81 | + database_password=$(yq .services.database.environment.POSTGRES_PASSWORD docker-compose.yml | |
| 82 | + perl -pe "s/'/''/g") |
| 83 | + echo "ALTER USER zulip WITH PASSWORD '$database_password';" | |
| 84 | + docker exec -i "$temp_container" psql -U zulip |
| 85 | +fi |
| 86 | + |
| 87 | +# Stop the running database |
| 88 | +docker-compose rm --force --stop database |
| 89 | + |
| 90 | +# Stop the temporary PostgreSQL container |
| 91 | +docker stop "$temp_container" |
| 92 | +docker rm "$temp_container" |
| 93 | +trap '' EXIT |
| 94 | + |
| 95 | +# Update the docker-compose.yml file for the new version and data path |
| 96 | +IMAGE="zulip/zulip-postgresql:$new_version" yq -i '.services.database.image = strenv(IMAGE)' docker-compose.yml |
| 97 | +VOLUME="$new_volume:/var/lib/postgresql/data:rw" yq -i '.services.database.volumes.0 = strenv(VOLUME)' docker-compose.yml |
| 98 | +VOLUME="$new_volume" yq -i 'with(.volumes.[strenv(VOLUME)]; . = "" | . tag="!!null")' docker-compose.yml |
| 99 | + |
| 100 | +# Restart zulip, and the new database container |
| 101 | +docker-compose up --wait |
| 102 | + |
| 103 | +echo "Old data from PostgreSQL $old_version is available in $old_mountpoint" |
0 commit comments