From 59717dc58991418bb81a87bd1bbf08e60ebbfc68 Mon Sep 17 00:00:00 2001 From: Rich Bayliss Date: Tue, 7 Apr 2020 12:49:18 +0100 Subject: [PATCH] upgrade: Container upgrades existing data on start 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 Change-type: minor --- Dockerfile | 4 ++ balena-entrypoint.sh | 118 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100755 balena-entrypoint.sh diff --git a/Dockerfile b/Dockerfile index 136648b..8d64216 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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" ] diff --git a/balena-entrypoint.sh b/balena-entrypoint.sh new file mode 100755 index 0000000..e1078f8 --- /dev/null +++ b/balena-entrypoint.sh @@ -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 "$@" \ No newline at end of file