From dd9c11fdcf9a97fb37e4246ae8795257fd51e668 Mon Sep 17 00:00:00 2001 From: Rizky Maulana Nugraha Date: Thu, 19 Aug 2021 20:55:32 +0700 Subject: [PATCH] fix #321: POSTGRES_INITDB_WALDIR handling (#324) This is mainly to fix #321 Fix POSTGRES_INITDB_WALDIR behaviour as expected for the default setup. With added several minor improvements: * Add scenario tests for testing POSTGRES_INITDB_WALDIR possible setup * Improve README.md * Fix symlink resolution check --- .github/workflows/build-latest.yaml | 137 ++++-------------- .github/workflows/deploy-image.yaml | 27 ++-- README.md | 6 +- .../datadir_init/docker-compose.yml | 83 +++++++++++ scenario_tests/datadir_init/test.sh | 5 +- .../datadir_init/test_custom_waldir.sh | 110 ++++++++++++++ .../datadir_init/tests/test_datadir.py | 33 +++++ scripts/env-data.sh | 8 +- scripts/setup-database.sh | 64 +++++++- 9 files changed, 334 insertions(+), 139 deletions(-) create mode 100644 scenario_tests/datadir_init/test_custom_waldir.sh diff --git a/.github/workflows/build-latest.yaml b/.github/workflows/build-latest.yaml index b843bc3..2eb89b0 100644 --- a/.github/workflows/build-latest.yaml +++ b/.github/workflows/build-latest.yaml @@ -14,72 +14,13 @@ on: # description: Base image variant # required: true # default: slim - push: + workflow_dispatch: pull_request: branches: - develop jobs: - build-image: - runs-on: ubuntu-latest - env: - latest-ref: refs/heads/develop - defaultRepo: kartoza - strategy: - matrix: - postgresMajorVersion: - - 13 - postgisMajorVersion: - - 3 - postgisMinorRelease: - - 1 - include: - - distro: debian - imageVersion: bullseye - imageVariant: slim - steps: - - uses: actions/checkout@v2 - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - name: Get build cache - uses: actions/cache@v2 - with: - path: /tmp/.buildx-cache - # Build inputs are: - # - Dockerfile recipe - # - docker-compose.build.yml - # - build args (.example.env) - # - base_build directory - # - scripts directory - key: buildx-${{ hashFiles('Dockerfile', 'docker-compose.build.yml', '.example.env', 'base_build', 'scripts', 'scenario_tests/utils/requirements.txt') }}-${{ github.sha }} - restore-keys: | - buildx-${{ hashFiles('Dockerfile', 'docker-compose.build.yml', '.example.env', 'base_build', 'scripts', 'scenario_tests/utils/requirements.txt') }}- - - - name: Build all stages - id: docker_build_base - uses: docker/build-push-action@v2 - with: - context: . - file: Dockerfile - push: false - load: true - tags: ${{ secrets.DOCKERHUB_REPO || env.defaultRepo }}/postgis:base-${{ matrix.distro }}-${{ matrix.imageVersion }}-${{ matrix.imageVariant }} - build-args: | - DISTRO=${{ matrix.distro }} - IMAGE_VERSION=${{ matrix.imageVersion }} - IMAGE_VARIANT=${{ matrix.imageVariant }} - LANGS=en_US.UTF-8,id_ID.UTF-8 - GENERATE_ALL_LOCALE=0 - POSTGRES_MAJOR_VERSION=${{ matrix.postgresMajorVersion }} - POSTGIS_MAJOR_VERSION=${{ matrix.postgisMajorVersion }} - POSTGIS_MINOR_VERSION=${{ matrix.postgresMinorVersion }} - cache-from: type=local,src=/tmp/.buildx-cache - cache-to: type=local,mode=max,dest=/tmp/.buildx-cache - run-scenario-tests: runs-on: ubuntu-latest - needs: [ build-image ] strategy: matrix: postgresMajorVersion: @@ -104,20 +45,6 @@ jobs: uses: docker/setup-qemu-action@v1 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - - name: Get build cache - uses: actions/cache@v2 - with: - path: /tmp/.buildx-cache - # Build inputs are: - # - Dockerfile recipe - # - docker-compose.build.yml - # - build args (.example.env) - # - base_build directory - # - scripts directory - key: buildx-${{ hashFiles('Dockerfile', 'docker-compose.build.yml', '.example.env', 'base_build', 'scripts', 'scenario_tests/utils/requirements.txt') }}-${{ github.sha }} - restore-keys: | - buildx-${{ hashFiles('Dockerfile', 'docker-compose.build.yml', '.example.env', 'base_build', 'scripts', 'scenario_tests/utils/requirements.txt') }}- - - name: Build image for testing id: docker_build_testing_image uses: docker/build-push-action@v2 @@ -136,22 +63,25 @@ jobs: POSTGRES_MAJOR_VERSION=${{ matrix.postgresMajorVersion }} POSTGIS_MAJOR_VERSION=${{ matrix.postgisMajorVersion }} POSTGIS_MINOR_VERSION=${{ matrix.postgresMinorVersion }} - cache-from: type=local,src=/tmp/.buildx-cache + cache-from: | + type=gha,scope=test + type=gha,scope=prod + type=gha,scope=base + cache-to: type=gha,scope=test target: postgis-test - - name: Run scenario test + - name: Run scenario test ${{ matrix.scenario }} + working-directory: scenario_tests/${{ matrix.scenario }} env: COMPOSE_INTERACTIVE_NO_CLI: 1 PRINT_TEST_LOGS: 1 run: | - pushd scenario_tests/${{ matrix.scenario }} - ./test.sh - popd + bash ./test.sh push-internal-pr-images: if: github.event_name == 'pull_request' && github.event.pull_request.base.repo.url == github.event.pull_request.head.repo.url runs-on: ubuntu-latest - needs: [ build-image ] + needs: [ run-scenario-tests ] strategy: matrix: postgresMajorVersion: @@ -165,37 +95,25 @@ jobs: imageVersion: bullseye imageVariant: slim steps: - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_PASSWORD }} - - - name: Docker meta - id: docker_meta - uses: crazy-max/ghaction-docker-meta@v1 - with: - images: ${{ secrets.DOCKERHUB_REPO}}/postgis - tag-latest: false - - uses: actions/checkout@v2 - name: Set up QEMU uses: docker/setup-qemu-action@v1 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - - name: Get build cache - uses: actions/cache@v2 + - name: Login to DockerHub + uses: docker/login-action@v1 with: - path: /tmp/.buildx-cache - # Build inputs are: - # - Dockerfile recipe - # - docker-compose.build.yml - # - build args (.example.env) - # - base_build directory - # - scripts directory - key: buildx-${{ hashFiles('Dockerfile', 'docker-compose.build.yml', '.example.env', 'base_build', 'scripts') }}-${{ github.sha }} - restore-keys: | - buildx-${{ hashFiles('Dockerfile', 'docker-compose.build.yml', '.example.env', 'base_build', 'scripts') }}- + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + - name: Docker meta + id: docker_meta + uses: docker/metadata-action@v3 + with: + images: ${{ secrets.DOCKERHUB_REPO}}/postgis + tags: | + type=semver,pattern=\d-\d.\d + type=ref,event=branch + type=ref,event=pr - name: Build image for testing id: docker_build_testing_image @@ -204,7 +122,8 @@ jobs: context: . file: Dockerfile push: true - tags: ${{ steps.docker_meta.outputs.tags }}-${{ matrix.postgresMajorVersion }}-${{ matrix.postgisMajorVersion }}.${{ matrix.postgisMinorRelease }} + tags: | + ${{ steps.docker_meta.outputs.tags }}-${{ matrix.postgresMajorVersion }}-${{ matrix.postgisMajorVersion }}.${{ matrix.postgisMinorRelease }} build-args: | DISTRO=${{ matrix.distro }} IMAGE_VERSION=${{ matrix.imageVersion }} @@ -214,5 +133,9 @@ jobs: POSTGRES_MAJOR_VERSION=${{ matrix.postgresMajorVersion }} POSTGIS_MAJOR_VERSION=${{ matrix.postgisMajorVersion }} POSTGIS_MINOR_VERSION=${{ matrix.postgresMinorVersion }} - cache-from: type=local,src=/tmp/.buildx-cache + cache-from: | + type=gha,scope=test + type=gha,scope=prod + type=gha,scope=base + cache-to: type=gha,scope=test target: postgis-test diff --git a/.github/workflows/deploy-image.yaml b/.github/workflows/deploy-image.yaml index 395ad61..608f599 100644 --- a/.github/workflows/deploy-image.yaml +++ b/.github/workflows/deploy-image.yaml @@ -44,19 +44,6 @@ jobs: uses: docker/setup-qemu-action@v1 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - - name: Get build cache - uses: actions/cache@v2 - with: - path: /tmp/.buildx-cache - # Build inputs are: - # - Dockerfile recipe - # - docker-compose.build.yml - # - build args (.example.env) - # - base_build directory - # - scripts directory - key: buildx-${{ hashFiles('Dockerfile', 'docker-compose.build.yml', '.example.env', 'base_build', 'scripts', 'scenario_tests/utils/requirements.txt') }}-${{ github.sha }} - restore-keys: | - buildx-${{ hashFiles('Dockerfile', 'docker-compose.build.yml', '.example.env', 'base_build', 'scripts', 'scenario_tests/utils/requirements.txt') }}- - name: Login to DockerHub uses: docker/login-action@v1 @@ -82,8 +69,11 @@ jobs: POSTGRES_MAJOR_VERSION=${{ matrix.postgresMajorVersion }} POSTGIS_MAJOR_VERSION=${{ matrix.postgisMajorVersion }} POSTGIS_MINOR_VERSION=${{ matrix.postgresMinorVersion }} - cache-from: type=local,src=/tmp/.buildx-cache - cache-to: type=local,dest=/tmp/.buildx-cache + cache-from: | + type=gha,scope=test + type=gha,scope=prod + type=gha,scope=base + cache-to: type=gha,scope=base target: postgis-base - name: Build prod image @@ -105,6 +95,9 @@ jobs: POSTGRES_MAJOR_VERSION=${{ matrix.postgresMajorVersion }} POSTGIS_MAJOR_VERSION=${{ matrix.postgisMajorVersion }} POSTGIS_MINOR_VERSION=${{ matrix.postgresMinorVersion }} - cache-from: type=local,src=/tmp/.buildx-cache - cache-to: type=local,dest=/tmp/.buildx-cache + cache-from: | + type=gha,scope=test + type=gha,scope=prod + type=gha,scope=base + cache-to: type=gha,scope=prod target: postgis-prod diff --git a/README.md b/README.md index 230a2e7..17cf290 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -[![Build Status](https://travis-ci.org/kartoza/docker-postgis.svg?branch=develop)](https://travis-ci.org/kartoza/docker-postgis) +[![Scenario Tests](https://github.com/kartoza/docker-postgis/actions/workflows/build-latest.yaml/badge.svg?branch=develop&event=push)](https://github.com/kartoza/docker-postgis/actions/workflows/build-latest.yaml) +[![deploy-image](https://github.com/kartoza/docker-postgis/actions/workflows/deploy-image.yaml/badge.svg)](https://github.com/kartoza/docker-postgis/actions/workflows/deploy-image.yaml) # docker-postgis @@ -143,7 +144,7 @@ You need to specify different empty directory, like this The containers will use above parameters to initialize a new db cluster in the specified directory. If the directory is not empty, then initialization parameter will be ignored. -These are some initialization parameters that will only get used to initialize a new cluster. +These are some initialization parameters that will only be used to initialize a new cluster. If the container uses an existing cluster, it is ignored (for example, when the container restarts). * `DEFAULT_ENCODING`: cluster encoding @@ -152,6 +153,7 @@ If the container uses an existing cluster, it is ignored (for example, when the * `WAL_SEGSIZE`: WAL segsize option * `PASSWORD_AUTHENTICATION` : PASSWORD AUTHENTICATION * `INITDB_EXTRA_ARGS`: extra parameter that will be passed down to `initdb` command +* `POSTGRES_INITDB_WALDIR`: parameter to tell postgres about the initial waldir location. Note that you must always mount persistent volume to this location. Postgres will expect that the directory will always be available, even though it doesn't need the environment variable anymore. If you didn't persist this location, postgres will not be able to find the `pg_wal` directory and consider the instance to be broken. In addition to that, we have another parameter: `RECREATE_DATADIR` that can be used to force database reinitializations. If this parameter is specified as `TRUE` it will act as explicit consent to delete `DATADIR` and create diff --git a/scenario_tests/datadir_init/docker-compose.yml b/scenario_tests/datadir_init/docker-compose.yml index d0c6198..9a1c4f0 100644 --- a/scenario_tests/datadir_init/docker-compose.yml +++ b/scenario_tests/datadir_init/docker-compose.yml @@ -2,7 +2,10 @@ version: '2.1' volumes: default-pg-data-dir: new-pg-data-dir: + new-pg-data-dir-2: + new-pg-data-dir-3: recreate-pg-data-dir: + init-waldir: services: pg-default: image: 'kartoza/postgis:${TAG:-manual-build}' @@ -58,3 +61,83 @@ services: timeout: 30s retries: 3 test: "pg_isready" + + pg-custom-waldir-wrong: + image: 'kartoza/postgis:${TAG:-manual-build}' + volumes: + # Mount to a locations where there are initial data + - new-pg-data-dir-2:/opt/mypostgis/data + # Specify different waldir location + - init-waldir:/opt/mypostgis/data/waldir + - ./tests:/tests + - ../utils:/lib/utils + environment: + TEST_CLASS: TestCustomWALdir + DATADIR: /opt/mypostgis/data + POSTGRES_INITDB_WALDIR: /opt/mypostgis/data/waldir + POSTGRES_PASS: 'docker' + healthcheck: + interval: 60s + timeout: 30s + retries: 3 + test: "pg_isready" + + pg-custom-waldir-correct: + image: 'kartoza/postgis:${TAG:-manual-build}' + volumes: + # Mount to new locations where there are no initial data + - new-pg-data-dir-3:/opt/mypostgis/data + # Specify different waldir location + - init-waldir:/opt/mypostgis/waldir + - ./tests:/tests + - ../utils:/lib/utils + environment: + TEST_CLASS: TestCustomWALdir + DATADIR: /opt/mypostgis/data + POSTGRES_INITDB_WALDIR: /opt/mypostgis/waldir + POSTGRES_PASS: 'docker' + healthcheck: + interval: 60s + timeout: 30s + retries: 3 + test: "pg_isready" + + pg-custom-waldir-not-match-1: + image: 'kartoza/postgis:${TAG:-manual-build}' + volumes: + # Mount to a locations where there are initial data + - new-pg-data-dir-3:/opt/mypostgis/data + # Specify different waldir location + - init-waldir:/opt/mypostgis/waldir + - ./tests:/tests + - ../utils:/lib/utils + environment: + TEST_CLASS: TestCustomWALdirNotMatch + DATADIR: /opt/mypostgis/data + POSTGRES_INITDB_WALDIR: /opt/waldir + POSTGRES_PASS: 'docker' + healthcheck: + interval: 60s + timeout: 30s + retries: 3 + test: "pg_isready" + + pg-custom-waldir-not-match-2: + image: 'kartoza/postgis:${TAG:-manual-build}' + volumes: + # Mount to a locations where there are initial data + - new-pg-data-dir-2:/opt/mypostgis/data + # Specify different waldir location + - init-waldir:/opt/waldir + - ./tests:/tests + - ../utils:/lib/utils + environment: + TEST_CLASS: TestCustomWALdirNotMatch + DATADIR: /opt/mypostgis/data + POSTGRES_INITDB_WALDIR: /opt/waldir + POSTGRES_PASS: 'docker' + healthcheck: + interval: 60s + timeout: 30s + retries: 3 + test: "pg_isready" \ No newline at end of file diff --git a/scenario_tests/datadir_init/test.sh b/scenario_tests/datadir_init/test.sh index 78b212c..d008ef7 100755 --- a/scenario_tests/datadir_init/test.sh +++ b/scenario_tests/datadir_init/test.sh @@ -6,7 +6,7 @@ set -e source ../test-env.sh # Run service -docker-compose up -d +docker-compose up -d pg-default pg-new pg-recreate if [[ -n "${PRINT_TEST_LOGS}" ]]; then docker-compose logs -f & @@ -28,4 +28,7 @@ for service in "${services[@]}"; do done +# special meta test to check the setup +bash ./test_custom_waldir.sh + docker-compose down -v diff --git a/scenario_tests/datadir_init/test_custom_waldir.sh b/scenario_tests/datadir_init/test_custom_waldir.sh new file mode 100644 index 0000000..63e48ff --- /dev/null +++ b/scenario_tests/datadir_init/test_custom_waldir.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash + +# exit immediately if test fails +set -e + +source ../test-env.sh + +# This test is special +# It is used to check the meta level of the setup. + +# Print logs +if [[ -n "${PRINT_TEST_LOGS}" ]]; then + docker-compose logs -f & +fi + +# Recreate containers with the same setup as pg-new and pg-default +# Try to make sure that container recreation is successful +echo "### Checking Container Recreation" +docker-compose down +docker-compose up -d pg-default pg-new pg-recreate + +sleep 60 + +services=("pg-default" "pg-new" "pg-recreate") + +for service in "${services[@]}"; do + + # Execute tests + until docker-compose exec -T $service pg_isready; do + sleep 5 + echo "Wait service to be ready" + done; + echo "Execute test for $service" + docker-compose exec -T $service /bin/bash /tests/test.sh + +done + +# Check the wrong setup must have warned that nested custom pg_wal location +# is prevented +echo "### Checking Error Message on nested pg_wal location" +service="pg-custom-waldir-wrong" +docker-compose up -d pg-custom-waldir-wrong + +sleep 60 + +# Loop until we found error message +while true; do + if [[ -n "$(docker-compose logs $service | grep 'Error')" && \ + -n "$(docker-compose logs $service | grep 'POSTGRES_INITDB_WALDIR should not be set to be inside DATADIR or PGDATA.')" ]]; then + break + fi + sleep 5 +done; + +docker-compose down + +# Check that the correct custom initdb waldir works, twice after container restart. +echo "### Checking custom POSTGRES_INITDB_WALDIR should work" +service="pg-custom-waldir-correct" +for ((i=1;i<=2;i++)); do + echo "attempt $i" + docker-compose up -d $service + sleep 60 + until docker-compose exec -T $service pg_isready; do + sleep 5 + echo "Wait service to be ready" + done; + echo "Execute test for $service" + docker-compose exec -T $service /bin/bash /tests/test.sh + docker-compose down +done + +# Check that if the variable POSTGRES_INITBD_WALDIR doesn't match with pg_wal symlink, +# then give warning, but proceeds if the the mount is still correct +echo "### Checking raise warning if custom POSTGRES_INITDB_WALDIR does not match" +service="pg-custom-waldir-not-match-1" +docker-compose up -d $service +sleep 60 +# Loop until we found warning message +while true; do + if [[ -n "$(docker-compose logs $service | grep 'Warning')" && \ + -n "$(docker-compose logs $service | grep 'POSTGRES_INITDB_WALDIR is not the same as what pg_wal is pointing to.')" ]]; then + break + fi + sleep 5 +done; +until docker-compose exec -T $service pg_isready; do + sleep 5 + echo "Wait service to be ready" +done; +echo "Execute test for $service" +docker-compose exec -T $service /bin/bash /tests/test.sh +docker-compose down + +# Check that if the pg_wal is empty, then something is wrong and we should exit +echo "### Checking Error and Exit if pg_wal is empty" +service="pg-custom-waldir-not-match-2" +docker-compose up -d $service +sleep 60 +# Loop until we found warning message +warning_text="Can't proceed because \"/opt/mypostgis/data/pg_wal\" directory is empty." +while true; do + if [[ -n "$(docker-compose logs $service | grep 'Error')" && \ + -n "$(docker-compose logs $service | grep "$warning_text")" ]]; then + break + fi + sleep 5 +done; + +docker-compose down -v diff --git a/scenario_tests/datadir_init/tests/test_datadir.py b/scenario_tests/datadir_init/tests/test_datadir.py index 99897b0..1bef318 100644 --- a/scenario_tests/datadir_init/tests/test_datadir.py +++ b/scenario_tests/datadir_init/tests/test_datadir.py @@ -1,6 +1,7 @@ import unittest import os from utils.utils import DBConnection +from pathlib import Path class TestCollationBase(unittest.TestCase): @@ -78,3 +79,35 @@ class TestRecreate(TestCollationBase): self.assertEqual(dbcollate, os.environ.get('DEFAULT_COLLATION')) self.assertEqual(dbctype, os.environ.get('DEFAULT_CTYPE')) + +class TestCustomWALdir(TestCollationBase): + + def test_check_pg_wal_symlink(self): + self.db.conn.autocommit = True + postgres_initdb_waldir = os.environ.get('POSTGRES_INITDB_WALDIR') + with self.db.cursor() as c: + datadir_location = self.fetch_datadir_location(c) + pg_wal_symlink = os.path.join(datadir_location, 'pg_wal') + # In this correct setup, pg_wal symlink must resolve to the + # correct POSTGRES_INITDB_WALDIR location + self.assertTrue( + Path(pg_wal_symlink).resolve().match(postgres_initdb_waldir)) + + +class TestCustomWALdirNotMatch(TestCollationBase): + + def test_check_pg_wal_symlink(self): + self.db.conn.autocommit = True + postgres_initdb_waldir = os.environ.get('POSTGRES_INITDB_WALDIR') + with self.db.cursor() as c: + datadir_location = self.fetch_datadir_location(c) + pg_wal_symlink = os.path.join(datadir_location, 'pg_wal') + # In this wrong setup, pg_wal symlink and POSTGRES_INITDB_WALDIR + # must resolve to a different path to raise the warning + self.assertFalse( + Path(pg_wal_symlink).resolve().match(postgres_initdb_waldir)) + + # It has different path, but if this unittests runs, that means + # postgres was able to start and the pg_wal symlink contains correct + # data + self.assertTrue(os.listdir(pg_wal_symlink)) diff --git a/scripts/env-data.sh b/scripts/env-data.sh index c9ebdeb..a5069cf 100644 --- a/scripts/env-data.sh +++ b/scripts/env-data.sh @@ -3,7 +3,9 @@ POSTGRES_MAJOR_VERSION=$(cat /tmp/pg_version.txt) POSTGIS_MAJOR=$(cat /tmp/pg_major_version.txt) POSTGIS_MINOR_RELEASE=$(cat /tmp/pg_minor_version.txt) DEFAULT_DATADIR="/var/lib/postgresql/${POSTGRES_MAJOR_VERSION}/main" -DEFAULT_INITDB_WALDIR="/var/lib/postgresql/${POSTGRES_MAJOR_VERSION}/pg_waldir" +# Commented for documentation. You can specify the location of +# pg_wal directory/volume using the following environment variable: +# POSTGRES_INITDB_WALDIR (default value is unset) ROOT_CONF="/etc/postgresql/${POSTGRES_MAJOR_VERSION}/main" PG_ENV="$ROOT_CONF/environment" CONF="$ROOT_CONF/postgresql.conf" @@ -71,10 +73,6 @@ fi } -if [ -z "${POSTGRES_INITDB_WALDIR}" ]; then - POSTGRES_INITDB_WALDIR=${DEFAULT_INITDB_WALDIR} -fi - # Make sure we have a user set up if [ -z "${POSTGRES_USER}" ]; then POSTGRES_USER=docker diff --git a/scripts/setup-database.sh b/scripts/setup-database.sh index 281f933..26d72c7 100644 --- a/scripts/setup-database.sh +++ b/scripts/setup-database.sh @@ -3,26 +3,76 @@ source /scripts/env-data.sh POSTGRES_PASS=$(cat /tmp/PGPASSWORD.txt) +INITDB_WALDIR_FLAG="" + +# Check POSTGRES_INITDB_WALDIR value +if [[ -n "${POSTGRES_INITDB_WALDIR}" ]]; then + # If POSTGRES_INITDB_WALDIR is defined, make sure that it is not inside + # the ${DATADIR} directory, to avoid deletions + case "${POSTGRES_INITDB_WALDIR}" in + ${DATADIR}/*) + # In this case, we have to fail early + echo "POSTGRES_INITDB_WALDIR should not be set to be inside DATADIR or PGDATA" +cat << EOF 1>&2 +Error! +POSTGRES_INITDB_WALDIR should not be set to be inside DATADIR or PGDATA. +POSTGRES_INITDB_WALDIR: ${POSTGRES_INITDB_WALDIR} +DATADIR or PGDATA: ${DATADIR} +EOF + exit 1 + ;; + *) + # For other case, make sure the directory is created with proper permissions + create_dir "${POSTGRES_INITDB_WALDIR}" + chown -R postgres:postgres ${POSTGRES_INITDB_WALDIR} + ;; + esac + # Set the --waldir flag for postgres initialization + INITDB_WALDIR_FLAG="--waldir ${POSTGRES_INITDB_WALDIR}" +fi # test if DATADIR has content -# Do initialization if DATADIR is empty, or RECREATE_DATADIR is true -if [[ -z "$(ls -A ${DATADIR} 2> /dev/null)" || "${RECREATE_DATADIR}" == 'TRUE' ]]; then +# Do initialization if DATADIR directory is empty, or RECREATE_DATADIR is true +if [[ -z "$(ls -A ${DATADIR} 2> /dev/null)" || "${RECREATE_DATADIR}" =~ [Tt][Rr][Uu][Ee] ]]; then # Only attempt reinitializations if ${RECREATE_DATADIR} is true # No Replicate From settings. Assume that this is a master database. # Initialise db echo "Initializing Postgres Database at ${DATADIR}" - create_dir ${POSTGRES_INITDB_WALDIR} - create_dir ${DATADIR} + create_dir "${DATADIR}" rm -rf ${DATADIR}/* - chown -R postgres:postgres ${DATADIR} ${POSTGRES_INITDB_WALDIR} + chown -R postgres:postgres "${DATADIR}" echo "Initializing with command:" - command="$INITDB -U postgres --pwfile=<(echo "$POSTGRES_PASS") -E ${DEFAULT_ENCODING} --lc-collate=${DEFAULT_COLLATION} --lc-ctype=${DEFAULT_CTYPE} --wal-segsize=${WAL_SEGSIZE} --auth=${PASSWORD_AUTHENTICATION} -D ${DATADIR} --waldir=${POSTGRES_INITDB_WALDIR} ${INITDB_EXTRA_ARGS}" + command="$INITDB -U postgres --pwfile=<(echo "$POSTGRES_PASS") -E ${DEFAULT_ENCODING} --lc-collate=${DEFAULT_COLLATION} --lc-ctype=${DEFAULT_CTYPE} --wal-segsize=${WAL_SEGSIZE} --auth=${PASSWORD_AUTHENTICATION} -D ${DATADIR} ${INITDB_WALDIR_FLAG} ${INITDB_EXTRA_ARGS}" + echo "$command" su - postgres -c "$command" +else + # If using existing datadir: + # Check if pg_wal symlink point to the correct directory described by POSTGRES_INITDB_WALDIR. + # Give warning if the value is not the same + if [[ -n "${POSTGRES_INITDB_WALDIR}" && \ + "$(realpath ${POSTGRES_INITDB_WALDIR})" != "$(realpath "$(readlink ${DATADIR}/pg_wal)")" ]]; then +cat << EOF 1>&2 +Warning! +POSTGRES_INITDB_WALDIR is not the same as what pg_wal is pointing to. +POSTGRES_INITDB_WALDIR: ${POSTGRES_INITDB_WALDIR} +pg_wal: $(readlink ${DATADIR}/pg_wal) +EOF + fi + + # Check if the pg_wal is empty. + # Exit the process if pg_wal is somehow empty + if [[ -z "$(ls -A ${DATADIR}/pg_wal 2> /dev/null)" ]]; then +cat << EOF 1>&2 +Error! +Can't proceed because "${DATADIR}/pg_wal" directory is empty. +EOF + exit 1 + fi fi; # Set proper permissions # needs to be done as root: -create_dir ${WAL_ARCHIVE} +create_dir "${WAL_ARCHIVE}" chown -R postgres:postgres ${DATADIR} ${WAL_ARCHIVE} chmod -R 750 ${DATADIR} ${WAL_ARCHIVE}