Skip to content

Commit

Permalink
upgrade: Container upgrades existing data on start
Browse files Browse the repository at this point in the history
Postgres will not automatically upgrade existing data when moving
to a different major version. These changes detect existing data
and compare their major versions to see if an upgrade is required.
If the upgrade is required then the correct tooling for the existing
data is installed and `pg_upgrade` is run.

Also part of this PR is the restructure of the data volume so that a
versioned sub-directory is used instead of the root of the mount. This
means that as you upgrade major versions you will keep a version of your
data which can be used.

Signed-off-by: Rich Bayliss <[email protected]>
Change-type: minor
  • Loading branch information
Rich Bayliss committed Apr 8, 2020
1 parent 67d5577 commit 59717dc
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ ENV POSTGRES_USER docker
ENV POSTGRES_PASSWORD docker

COPY create-resin-db.sh /docker-entrypoint-initdb.d/
COPY balena-entrypoint.sh /balena-entrypoint.sh

CMD [ "postgres" ]
ENTRYPOINT [ "/balena-entrypoint.sh" ]
118 changes: 118 additions & 0 deletions balena-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#!/bin/bash

set -e

# creates a backup archive of the dir $1...
backup_data () {
gosu postgres tar --exclude backup.tar.gz -zcf /tmp/backup.tar.gz -C "$1" .
mv /tmp/backup.tar.gz "${1}/backup.tar.gz"
}

# copies all the data from $1 to $2...
restore_volume () {
echo "=== Restoring data volume structure"
find "$1" -maxdepth 1 \
! -path "$1" \
| xargs mv -t "$2"
}

# install the old version of Postgres, required when doing an upgrade...
install_old_version () {
if [ ! -d "/usr/lib/postgresql/$1/bin" ]; then
sed -i 's/$/ '"$1"'/' /etc/apt/sources.list.d/pgdg.list

apt-get -qq update \
&& apt-get install -qq -y --no-install-recommends \
"postgresql-$1" \
&& rm -rf /var/lib/apt/lists/*
fi
}

# creates a dir at $1 which is valid for Postgres data to live in...
create_postgres_data_dir () {
echo "=== Creating data dir $1"
mkdir -p "$1"
chmod 700 "$1"
chown -R postgres:postgres "$1"
}

# echo a message and die...
die_with_message () {
echo "[FATAL] $1"
exit 2
}

# check that this is running from within a valid Postgres container environment...
[ ! -z "$PG_MAJOR" ] || die_with_message "Not a compatible Postgres runtime environment"

# set our target version...
TARGET_VERSION="$PG_MAJOR"

# ensure we have a versioned data directory...
[ -d "${PGDATA}/${TARGET_VERSION}" ] || create_postgres_data_dir "${PGDATA}/${TARGET_VERSION}"

# check for Postgres data in the root of the $PGDATA directory...
if [ -f "${PGDATA}/PG_VERSION" ]; then
SOURCE_VERSION="$(cat ${PGDATA}/PG_VERSION)"

# does the data need upgrading...
if [ "$SOURCE_VERSION" -ne "$TARGET_VERSION" ]; then
echo "=== Upgrading data from v${SOURCE_VERSION} to v${TARGET_VERSION}"

# define our directories...
PGDATAOLD="${PGDATA}/${SOURCE_VERSION}"
PGDATANEW="${PGDATA}/${TARGET_VERSION}"
PGBINOLD="/usr/lib/postgresql/$SOURCE_VERSION/bin"
PGBINNEW="/usr/lib/postgresql/$TARGET_VERSION/bin"

echo "=== Installing tools for Postgres v${SOURCE_VERSION}"
install_old_version "$SOURCE_VERSION"

echo "=== Moving extisting data to ${PGDATAOLD}"
create_postgres_data_dir "${PGDATAOLD}"
find "$PGDATA" -maxdepth 1 \
! -path "$PGDATA" \
! -path "$PGDATAOLD*" \
| xargs mv -t "${PGDATAOLD}"

trap "restore_volume ${PGDATAOLD} ${PGDATA}" ERR

echo "=== Initialising new data directory ${PGDATANEW}"
rm -rf "${PGDATANEW}"
create_postgres_data_dir "${PGDATANEW}"
gosu postgres initdb -D "$PGDATANEW" -U "$POSTGRES_USER" $POSTGRES_INITDB_ARGS

echo "=== Beginning pg_upgrade"
cd /tmp
gosu postgres pg_upgrade \
-U "$POSTGRES_USER" \
--old-datadir="$PGDATAOLD" \
--new-datadir="$PGDATANEW" \
--old-bindir="$PGBINOLD" \
--new-bindir="$PGBINNEW" \
--check

gosu postgres pg_upgrade \
-U "$POSTGRES_USER" \
--old-datadir="$PGDATAOLD" \
--new-datadir="$PGDATANEW" \
--old-bindir="$PGBINOLD" \
--new-bindir="$PGBINNEW"

echo "=== Restoring configuration files"
cp "${PGDATAOLD}/pg_hba.conf" "${PGDATANEW}/pg_hba.conf"
cp "${PGDATAOLD}/pg_ident.conf" "${PGDATANEW}/pg_ident.conf"
else
echo "=== Moving extisting data to directory "${PGDATA}/${TARGET_VERSION}""
find "$PGDATA" -maxdepth 1 \
! -path "$PGDATA" \
| xargs mv -t "${PGDATA}/${TARGET_VERSION}"
fi
fi

# set our runtime data directory to the versioned one...
export PGDATA="${PGDATA}/${TARGET_VERSION}"

# run the existing Postgres entrypoint script...
. /docker-entrypoint.sh
_main "$@"

0 comments on commit 59717dc

Please sign in to comment.