Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions deploy/docker/base.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ RUN set -o xtrace \
&& echo "deb http://apt.postgresql.org/pub/repos/apt $(grep CODENAME /etc/lsb-release | cut -d= -f2)-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list \
&& curl --silent --show-error --location https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \
&& apt update \
&& apt-get install --no-install-recommends --yes mongodb-org redis postgresql-13 \
&& apt-get install --no-install-recommends --yes mongodb-org redis postgresql-13 postgresql-14 \
# Create a symlink to the current version of PostgreSQL
&& ln -s /usr/lib/postgresql/13 /usr/lib/postgresql/current \
&& apt-get clean

ENV PATH="/usr/lib/postgresql/13/bin:${PATH}"
ENV PATH="/usr/lib/postgresql/14/bin:${PATH}"

# Install Java
RUN set -o xtrace \
Expand Down
7 changes: 3 additions & 4 deletions deploy/docker/fs/opt/appsmith/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ set -e

tlog "Running as: $(id)"

# Temporary, remove after this change goes into `base.dockerfile`.
export PATH="/usr/lib/postgresql/13/bin:${PATH}"

stacks_path=/appsmith-stacks

export SUPERVISORD_CONF_TARGET="$TMP/supervisor-conf.d/" # export for use in supervisord.conf
Expand Down Expand Up @@ -428,7 +425,9 @@ init_postgres() {
# Postgres does not allow it's server to be run with super user access, we use user postgres and the file system owner also needs to be the same user postgres
chown -R postgres:postgres "$POSTGRES_DB_PATH" "$TMP/pg-runtime"

if [[ ! -e "$POSTGRES_DB_PATH/PG_VERSION" ]]; then
if [[ -e "$POSTGRES_DB_PATH/PG_VERSION" ]]; then
/opt/appsmith/pg-upgrade.sh
else
tlog "Initializing local Postgres data folder"
su postgres -c "env PATH='$PATH' initdb -D $POSTGRES_DB_PATH"
fi
Expand Down
95 changes: 95 additions & 0 deletions deploy/docker/fs/opt/appsmith/pg-upgrade.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#!/bin/bash

set -o errexit
set -o nounset

# This script will upgrade Postgres to the "current" version of Postgres, if needed.

# Assumptions:
# 1. Postgres is currently not running. The caller of this script ensures that _no_ version of Postgres server is currently running.
# 2. The newest version of Postgres we want to use, is already installed.
# 3. Postgres is installed via apt package manager only.

# Contract:
# 1. Don't install old version of Postgres, if it's already installed.
# 2. Use absolute paths to all Postgres executables, don't rely on any of them to be coming from "\$PATH".
# 3. Be idempotent across versions.
# 4. When we can't proceed due to any exceptional scenarios, communicate clearly.
# 5. Mark old/stale/deprecated data with a date, so it can be deleted with confidence later.

# Check if any Postgres server is running
if pgrep -x "postgres" > /dev/null; then
echo "Error: A Postgres server is currently running. Please stop it before proceeding with the upgrade."
exit 1
fi

postgres_path=/usr/lib/postgresql

pg_data_dir=/appsmith-stacks/data/postgres/main

old_version=""
if [[ -f "$pg_data_dir/PG_VERSION" ]]; then
old_version="$(cat "$pg_data_dir/PG_VERSION")"
fi

if [[ -z "$old_version" ]]; then
tlog "No existing Postgres data found, not upgrading anything." >&2
exit
fi

if [[ -f "$pg_data_dir/postmaster.pid" ]]; then
tlog "Previous Postgres was not shutdown cleanly. Please start and stop Postgres $old_version properly with 'supervisorctl' only." >&2
exit 1
fi

top_available_version="$(postgres --version | grep -o '[[:digit:]]\+' | head -1)"

declare -a to_uninstall
to_uninstall=()

# 13 to 14
if [[ "$old_version" == 13 && "$top_available_version" > "$old_version" ]]; then
if [[ ! -e "$postgres_path/$old_version" ]]; then
apt-get update
apt-get install --yes "postgresql-$old_version"
to_uninstall+=("postgresql-$old_version")
fi

new_version="$((old_version + 1))"
new_data_dir="$pg_data_dir-$new_version"

# `pg_upgrade` writes log to current folder. So change to a temp folder first.
rm -rf "$TMP/pg_upgrade" "$new_data_dir"
mkdir -p "$TMP/pg_upgrade" "$new_data_dir"
chown -R postgres "$TMP/pg_upgrade" "$new_data_dir"
cd "$TMP/pg_upgrade"

# Required by the temporary Postgres server started by `pg_upgrade`.
chown postgres /etc/ssl/private/ssl-cert-snakeoil.key
chmod 0600 /etc/ssl/private/ssl-cert-snakeoil.key

su postgres --command "
set -o errexit
set -o xtrace
'$postgres_path/$new_version/bin/initdb' --pgdata='$new_data_dir'
'$postgres_path/$new_version/bin/pg_upgrade' \
--old-datadir='$pg_data_dir' \
--new-datadir='$new_data_dir' \
--old-bindir='$postgres_path/$old_version/bin' \
--new-bindir='$postgres_path/$new_version/bin'
"

date -u '+%FT%T.%3NZ' > "$pg_data_dir/deprecated-on.txt"
mv -v "$pg_data_dir" "$pg_data_dir-$old_version"
mv -v "$new_data_dir" "$pg_data_dir"

# Dangerous generated script that deletes the now updated data folder.
rm -fv "$TMP/pg_upgrade/delete_old_cluster.sh"
fi

if [[ -n "${#to_uninstall[@]}" ]]; then
apt-get purge "${to_uninstall[@]}"
apt-get clean
fi

echo "== Fin =="
93 changes: 93 additions & 0 deletions deploy/docker/tests/pg-upgrade/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/bin/bash

# A script to test Postgres upgrades. WIP.

set -o errexit
set -o nounset
set -o xtrace

from_tag=appsmith/appsmith-ce:v1.28
to_tag=appsmith/appsmith-ce:latest

container_name=appsmith-pg-upgrade-test
port=20080

docker rm -f "$container_name"
docker volume rm --force "$container_name"

# TODO: Add `--pull always` for images that have a manifest?

docker volume create "$container_name"
docker run \
--name "$container_name" \
--detach \
--publish "$port":80 \
--volume "$container_name":/appsmith-stacks \
"$from_tag"

wait-for-supervisor() {
while ! docker exec "$container_name" test -e /tmp/appsmith/supervisor.sock; do
sleep 1
done
sleep 2
}

wait-for-supervisor

docker exec "$container_name" bash -exc '
supervisorctl status \
| awk '\''$1 != "postgres" && $1 != "stdout" {print $1}'\'' \
| xargs supervisorctl stop

# Insert some sample data
su postgres -c "psql -h 127.0.0.1 -c \"
create table t (id serial, name text);
insert into t values (1, '\''one'\'');
insert into t values (2, '\''two'\'');
insert into t values (3, '\''three'\'');
\""

supervisorctl stop postgres

cat /appsmith-stacks/data/postgres/main/PG_VERSION
'

docker rm -f "$container_name"

docker run \
--name "$container_name" \
--detach \
--publish "$port":80 \
--volume "$container_name":/appsmith-stacks \
"$to_tag"

wait-for-supervisor

status=0

if [[ 14 != "$(docker exec "$container_name" cat /appsmith-stacks/data/postgres/main/PG_VERSION)" ]]; then
echo "Version isn't 14"
status=1
else
sample_table_contents="$(su postgres -c 'psql -h 127.0.0.1 -c "select * from t"')"
expected_contents=' id | name
----+-------
1 | one
2 | two
3 | three
(3 rows)'
if ! diff <(echo "$expected_contents") <(su postgres -c 'psql -h 127.0.0.1 -c "select * from t"'); then
status=1
echo "Table contents mismatch. Found this:"
su postgres -c 'psql -h 127.0.0.1 -c "select * from t"'
echo "Instead of this:"
echo "$expected_contents"
fi
fi

docker exec -it "$container_name" bash

docker rm --force "$container_name"
docker volume rm --force "$container_name"

exit "$status"