kopia lustrzana https://github.com/kartoza/docker-postgis
Fix data directory deleted on migrations (#228)
* Fix data directory deleted on migrations * Fix issue #226 * Refactor entrypoint process to only delete DATADIR when RECREATE_DATADIR is explicitly set to TRUE * Refactor config to use DATADIR from environment variable * Update README * Refactor tests to use python * Add collations tests * Change travis config to omit docker image sharing * Change tests for collation default * Following up PR #228 about environment variable behaviour * Add tests for datadir initializations * Update Travis config * Improve replication testingpull/230/head^2
rodzic
f535c58085
commit
af1e88beab
|
@ -3,3 +3,6 @@
|
||||||
*/replication/pg-*
|
*/replication/pg-*
|
||||||
*/replication/docker-compose.override.yml
|
*/replication/docker-compose.override.yml
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.python-version
|
||||||
|
venv
|
||||||
|
__pycache__
|
||||||
|
|
28
.travis.yml
28
.travis.yml
|
@ -6,22 +6,18 @@ services:
|
||||||
- docker
|
- docker
|
||||||
|
|
||||||
python:
|
python:
|
||||||
- '2.7'
|
- '3.7'
|
||||||
- '3.5'
|
|
||||||
- '3.6.7'
|
env:
|
||||||
- '3.7.1'
|
- SCENARIO=datadir_init
|
||||||
|
- SCENARIO=replications
|
||||||
|
- SCENARIO=collations
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- ./build-test.sh
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ./build.sh
|
- pushd scenario_tests/${SCENARIO}
|
||||||
- pushd sample/replication
|
- ./test.sh
|
||||||
- make up
|
|
||||||
- make status
|
|
||||||
# Check for database status
|
|
||||||
- until make check-master-running; do echo "Retrying"; sleep 5; done
|
|
||||||
- until make check-slave-running; do echo "Retrying"; sleep 5; done
|
|
||||||
|
|
||||||
# Check replications
|
|
||||||
- until make check-master-replication; do echo "Retrying"; make master-log-tail; sleep 5; done
|
|
||||||
- sleep 60 # Wait replication finished
|
|
||||||
- until make check-slave-replication; do echo "Retrying"; make slave-log-tail; sleep 5; done
|
|
||||||
- popd
|
- popd
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
#--------- Generic stuff all our Dockerfiles should start with so we get caching ------------
|
||||||
|
FROM kartoza/postgis:manual-build
|
||||||
|
|
||||||
|
# For testing
|
||||||
|
|
||||||
|
COPY scenario_tests/utils/requirements.txt /lib/utils/requirements.txt
|
||||||
|
|
||||||
|
RUN set -eux \
|
||||||
|
&& export DEBIAN_FRONTEND=noninteractive \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get -y --no-install-recommends install python3-pip \
|
||||||
|
&& apt-get -y --purge autoremove \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN pip3 install -r /lib/utils/requirements.txt
|
67
README.md
67
README.md
|
@ -91,6 +91,50 @@ docker run --name "postgis" -p 25432:5432 -d -t kartoza/postgis
|
||||||
|
|
||||||
## Environment variables
|
## Environment variables
|
||||||
|
|
||||||
|
#### Cluster Initializations
|
||||||
|
|
||||||
|
With minimum setup, our image will use initial cluster located in the
|
||||||
|
`DATADIR` environment variable. If you want to use persistence, mount these
|
||||||
|
location into your volume/host. By default, `DATADIR` will point to `/var/lib/postgresql/{major-version}`.
|
||||||
|
You can instead mount the parent location like this:
|
||||||
|
|
||||||
|
* `-v data-volume:/var/lib/postgresql`
|
||||||
|
|
||||||
|
This default cluster will be initialized with default locale settings `C.UTF-8`.
|
||||||
|
If, for instance, you want to create a new cluster with your own settings (not using the default cluster).
|
||||||
|
You need to specify different empty directory, like this
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
-v data-volume:/opt/postgres/data \
|
||||||
|
-e DATADIR:/opt/postgres/data \
|
||||||
|
-e DEFAULT_ENCODING="UTF8" \
|
||||||
|
-e DEFAULT_COLLATION="id_ID.utf8" \
|
||||||
|
-e DEFAULT_CTYPE="id_ID.utf8" \
|
||||||
|
-e INITDB_EXTRA_ARGS="<some more initdb command args>"
|
||||||
|
```
|
||||||
|
|
||||||
|
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 parameter that will only be used to initialize new cluster.
|
||||||
|
If the container uses existing cluster, it will be ignored (for example, when the container restarts).
|
||||||
|
|
||||||
|
* `DEFAULT_ENCODING`: cluster encoding
|
||||||
|
* `DEFAULT_COLLATION`: cluster collation
|
||||||
|
* `DEFAULT_CTYPE`: cluster ctype
|
||||||
|
* `WAL_SEGSIZE`: WAL segsize option
|
||||||
|
* `INITDB_EXTRA_ARGS`: extra parameter that will be passed down to `initdb` command
|
||||||
|
|
||||||
|
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
|
||||||
|
new db cluster.
|
||||||
|
|
||||||
|
* `RECREATE_DATADIR`: Force database reinitializations in the location `DATADIR`
|
||||||
|
|
||||||
|
If you used `RECREATE_DATADIR` and successfully created new cluster. Remember
|
||||||
|
that you should remove this parameter afterwards. Because, if it was not omitted,
|
||||||
|
it will always recreate new db cluster after every container restarts.
|
||||||
|
|
||||||
#### Basic configuration
|
#### Basic configuration
|
||||||
|
|
||||||
You can use the following environment variables to pass a
|
You can use the following environment variables to pass a
|
||||||
|
@ -152,10 +196,11 @@ You can also define any other configuration to add to `postgres.conf`, separated
|
||||||
|
|
||||||
* `-e EXTRA_CONF="log_destination = 'stderr'\nlogging_collector = on"`
|
* `-e EXTRA_CONF="log_destination = 'stderr'\nlogging_collector = on"`
|
||||||
|
|
||||||
If you plan on migrating the image and continue using the data directory you need to pass
|
If you want to reinitialize the data directory from scratch, you need to do:
|
||||||
|
|
||||||
* EXISTING_DATA_DIR=true
|
|
||||||
|
|
||||||
|
1. Do backup, move data, etc. Any preparations before deleting your data directory.
|
||||||
|
2. Set environment variables `RECREATE_DATADIR=TRUE`. Restart the service
|
||||||
|
3. The service will delete your `DATADIR` directory and start reinitializing your data directory from scratch.
|
||||||
|
|
||||||
## Docker secrets
|
## Docker secrets
|
||||||
|
|
||||||
|
@ -414,13 +459,25 @@ The database cluster is initialised with the following encoding settings
|
||||||
-E "UTF8" --lc-collate="en_US.UTF-8" --lc-ctype="en_US.UTF-8"
|
-E "UTF8" --lc-collate="en_US.UTF-8" --lc-ctype="en_US.UTF-8"
|
||||||
`
|
`
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
`
|
||||||
|
-E "UTF8" --lc-collate="C.UTF-8" --lc-ctype="C.UTF-8"
|
||||||
|
`
|
||||||
|
|
||||||
|
If you use default `DATADIR` location.
|
||||||
|
|
||||||
If you need to setup a database cluster with other encoding parameters you need
|
If you need to setup a database cluster with other encoding parameters you need
|
||||||
to pass the environment variables
|
to pass the environment variables when you initialize the cluster.
|
||||||
|
|
||||||
* -e DEFAULT_ENCODING="UTF8"
|
* -e DEFAULT_ENCODING="UTF8"
|
||||||
* -e DEFAULT_COLLATION="en_US.UTF-8"
|
* -e DEFAULT_COLLATION="en_US.UTF-8"
|
||||||
* -e DEFAULT_CTYPE="en_US.UTF-8"
|
* -e DEFAULT_CTYPE="en_US.UTF-8"
|
||||||
|
|
||||||
|
Initializing a new cluster can be done by using different `DATADIR` location and
|
||||||
|
mounting an empty volume. Or use parameter `RECREATE_DATADIR` to forcefully
|
||||||
|
delete the current cluster and create a new one. Make sure to remove parameter
|
||||||
|
`RECREATE_DATADIR` after creating the cluster.
|
||||||
|
|
||||||
See [the postgres documentation about encoding](https://www.postgresql.org/docs/11/multibyte.html) for more information.
|
See [the postgres documentation about encoding](https://www.postgresql.org/docs/11/multibyte.html) for more information.
|
||||||
|
|
||||||
|
@ -429,6 +486,6 @@ See [the postgres documentation about encoding](https://www.postgresql.org/docs/
|
||||||
|
|
||||||
Tim Sutton (tim@kartoza.com)
|
Tim Sutton (tim@kartoza.com)
|
||||||
Gavin Fleming (gavin@kartoza.com)
|
Gavin Fleming (gavin@kartoza.com)
|
||||||
Risky Maulana (rizky@kartoza.com)
|
Rizky Maulana (rizky@kartoza.com)
|
||||||
Admire Nyakudya (admire@kartoza.com)
|
Admire Nyakudya (admire@kartoza.com)
|
||||||
December 2018
|
December 2018
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
./build.sh
|
||||||
|
|
||||||
|
docker build -t kartoza/postgis:manual-build -f Dockerfile.test .
|
|
@ -1,8 +1,9 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# This script will run as the postgres user due to the Dockerfile USER directive
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
source /env-data.sh
|
||||||
|
|
||||||
# Setup postgres CONF file
|
# Setup postgres CONF file
|
||||||
|
|
||||||
source /setup-conf.sh
|
source /setup-conf.sh
|
||||||
|
@ -14,46 +15,6 @@ source /setup-ssl.sh
|
||||||
|
|
||||||
source /setup-pg_hba.sh
|
source /setup-pg_hba.sh
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Running extended script or sql if provided.
|
|
||||||
# Useful for people who extends the image.
|
|
||||||
function entry_point_script {
|
|
||||||
SETUP_LOCKFILE="/docker-entrypoint-initdb.d/.entry_point.lock"
|
|
||||||
if [[ -f "${SETUP_LOCKFILE}" ]]; then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
if find "/docker-entrypoint-initdb.d" -mindepth 1 -print -quit 2>/dev/null | grep -q .; then
|
|
||||||
for f in /docker-entrypoint-initdb.d/*; do
|
|
||||||
export PGPASSWORD=${POSTGRES_PASS}
|
|
||||||
case "$f" in
|
|
||||||
*.sql) echo "$0: running $f"; psql ${SINGLE_DB} -U ${POSTGRES_USER} -p 5432 -h localhost -f ${f} || true ;;
|
|
||||||
*.sql.gz) echo "$0: running $f"; gunzip < "$f" | psql ${SINGLE_DB} -U ${POSTGRES_USER} -p 5432 -h localhost || true ;;
|
|
||||||
*.sh) echo "$0: running $f"; . $f || true;;
|
|
||||||
*) echo "$0: ignoring $f" ;;
|
|
||||||
esac
|
|
||||||
echo
|
|
||||||
done
|
|
||||||
# Put lock file to make sure entry point scripts were run
|
|
||||||
touch ${SETUP_LOCKFILE}
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function kill_postgres {
|
|
||||||
PID=`cat ${PG_PID}`
|
|
||||||
kill -TERM ${PID}
|
|
||||||
|
|
||||||
# Wait for background postgres main process to exit
|
|
||||||
while [[ "$(ls -A ${PG_PID} 2>/dev/null)" ]]; do
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
if [[ -z "$REPLICATE_FROM" ]]; then
|
if [[ -z "$REPLICATE_FROM" ]]; then
|
||||||
# This means this is a master instance. We check that database exists
|
# This means this is a master instance. We check that database exists
|
||||||
echo "Setup master database"
|
echo "Setup master database"
|
||||||
|
|
103
env-data.sh
103
env-data.sh
|
@ -41,6 +41,17 @@ function file_env {
|
||||||
unset "$fileVar"
|
unset "$fileVar"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function boolean() {
|
||||||
|
case $1 in
|
||||||
|
[Tt][Rr][Uu][Ee] | [Yy][Ee][Ss])
|
||||||
|
echo 'TRUE'
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo 'FALSE'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
file_env 'POSTGRES_PASS'
|
file_env 'POSTGRES_PASS'
|
||||||
file_env 'POSTGRES_USER'
|
file_env 'POSTGRES_USER'
|
||||||
file_env 'POSTGRES_DBNAME'
|
file_env 'POSTGRES_DBNAME'
|
||||||
|
@ -55,6 +66,18 @@ fi
|
||||||
if [ -z "${POSTGRES_DBNAME}" ]; then
|
if [ -z "${POSTGRES_DBNAME}" ]; then
|
||||||
POSTGRES_DBNAME=gis
|
POSTGRES_DBNAME=gis
|
||||||
fi
|
fi
|
||||||
|
# If datadir is defined use this
|
||||||
|
if [ -n "${DATADIR}" ]; then
|
||||||
|
DATADIR=${DATADIR}
|
||||||
|
fi
|
||||||
|
# RECREATE_DATADIR flag default value
|
||||||
|
# Always assume that we don't want to recreate datadir if not explicitly defined
|
||||||
|
# For issue: https://github.com/kartoza/docker-postgis/issues/226
|
||||||
|
if [ -z "${RECREATE_DATADIR}" ]; then
|
||||||
|
RECREATE_DATADIR=FALSE
|
||||||
|
else
|
||||||
|
RECREATE_DATADIR=$(boolean ${RECREATE_DATADIR})
|
||||||
|
fi
|
||||||
# SSL mode
|
# SSL mode
|
||||||
if [ -z "${PGSSLMODE}" ]; then
|
if [ -z "${PGSSLMODE}" ]; then
|
||||||
PGSSLMODE=require
|
PGSSLMODE=require
|
||||||
|
@ -192,37 +215,79 @@ fi
|
||||||
|
|
||||||
# Compatibility with official postgres variable
|
# Compatibility with official postgres variable
|
||||||
# Official postgres variable gets priority
|
# Official postgres variable gets priority
|
||||||
if [ ! -z "${POSTGRES_PASSWORD}" ]; then
|
if [ -n "${POSTGRES_PASSWORD}" ]; then
|
||||||
POSTGRES_PASS=${POSTGRES_PASSWORD}
|
POSTGRES_PASS=${POSTGRES_PASSWORD}
|
||||||
fi
|
fi
|
||||||
if [ ! -z "${PGDATA}" ]; then
|
if [ -n "${PGDATA}" ]; then
|
||||||
DATADIR=${PGDATA}
|
DATADIR=${PGDATA}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -z "$POSTGRES_DB" ]; then
|
if [ -n "${POSTGRES_DB}" ]; then
|
||||||
POSTGRES_DBNAME=${POSTGRES_DB}
|
POSTGRES_DBNAME=${POSTGRES_DB}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -n "${POSTGRES_INITDB_ARGS}" ]; then
|
||||||
|
INITDB_EXTRA_ARGS=${POSTGRES_INITDB_ARGS}
|
||||||
|
fi
|
||||||
|
|
||||||
list=(`echo ${POSTGRES_DBNAME} | tr ',' ' '`)
|
list=(`echo ${POSTGRES_DBNAME} | tr ',' ' '`)
|
||||||
arr=(${list})
|
arr=(${list})
|
||||||
SINGLE_DB=${arr[0]}
|
SINGLE_DB=${arr[0]}
|
||||||
|
|
||||||
|
|
||||||
# usable function definitions
|
# usable function definitions
|
||||||
function restart_postgres {
|
function kill_postgres {
|
||||||
PID=`cat ${PG_PID}`
|
PID=`cat ${PG_PID}`
|
||||||
kill -TERM ${PID}
|
kill -TERM ${PID}
|
||||||
|
|
||||||
# Wait for background postgres main process to exit
|
# Wait for background postgres main process to exit
|
||||||
while [[ "$(ls -A ${PG_PID} 2>/dev/null)" ]]; do
|
# wait until PID file gets deleted
|
||||||
sleep 1
|
while ls -A ${PG_PID} 2> /dev/null; do
|
||||||
done
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
# Brought postgres back up again
|
return 0
|
||||||
source /env-data.sh
|
}
|
||||||
su - postgres -c "${POSTGRES} -D ${DATADIR} -c config_file=${CONF} ${LOCALONLY} &"
|
|
||||||
|
function restart_postgres {
|
||||||
# wait for postgres to come up
|
|
||||||
until su - postgres -c "psql -l"; do
|
kill_postgres
|
||||||
sleep 1
|
|
||||||
done
|
# Brought postgres back up again
|
||||||
echo "postgres ready"
|
source /env-data.sh
|
||||||
|
su - postgres -c "$SETVARS $POSTGRES -D $DATADIR -c config_file=$CONF &"
|
||||||
|
|
||||||
|
# wait for postgres to come up
|
||||||
|
until su - postgres -c "pg_isready"; do
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
echo "postgres ready"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Running extended script or sql if provided.
|
||||||
|
# Useful for people who extends the image.
|
||||||
|
function entry_point_script {
|
||||||
|
SETUP_LOCKFILE="/docker-entrypoint-initdb.d/.entry_point.lock"
|
||||||
|
# If lockfile doesn't exists, proceed.
|
||||||
|
if [[ ! -f "${SETUP_LOCKFILE}" ]]; then
|
||||||
|
if find "/docker-entrypoint-initdb.d" -mindepth 1 -print -quit 2>/dev/null | grep -q .; then
|
||||||
|
for f in /docker-entrypoint-initdb.d/*; do
|
||||||
|
export PGPASSWORD=${POSTGRES_PASS}
|
||||||
|
case "$f" in
|
||||||
|
*.sql) echo "$0: running $f"; psql ${SINGLE_DB} -U ${POSTGRES_USER} -p 5432 -h localhost -f ${f} || true ;;
|
||||||
|
*.sql.gz) echo "$0: running $f"; gunzip < "$f" | psql ${SINGLE_DB} -U ${POSTGRES_USER} -p 5432 -h localhost || true ;;
|
||||||
|
*.sh) echo "$0: running $f"; . $f || true;;
|
||||||
|
*) echo "$0: ignoring $f" ;;
|
||||||
|
esac
|
||||||
|
echo
|
||||||
|
done
|
||||||
|
# Put lock file to make sure entry point scripts were run
|
||||||
|
touch ${SETUP_LOCKFILE}
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
docker-compose.override.yml
|
|
@ -0,0 +1,75 @@
|
||||||
|
# TESTING GUIDE
|
||||||
|
|
||||||
|
|
||||||
|
## TL;DR; How to run the test
|
||||||
|
|
||||||
|
Go into root repo and run
|
||||||
|
|
||||||
|
```
|
||||||
|
./build-test.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
It will create a tagged image `kartoza/postgis:manual-build`
|
||||||
|
|
||||||
|
Each scenario tests in this directory use this image.
|
||||||
|
|
||||||
|
To run each scenario test, go into the scenario directory and run test script:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Testing collations scenario
|
||||||
|
cd collations
|
||||||
|
./test.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing architecture
|
||||||
|
|
||||||
|
We make two stage of testing in travis.
|
||||||
|
First build stage, we build the image, push it into docker hub.
|
||||||
|
Then the testing stage will
|
||||||
|
use this image to run each scenario tests (run in parallel).
|
||||||
|
|
||||||
|
Any testing framework can be used, but we use python `unittest` module for simplicity and readability.
|
||||||
|
|
||||||
|
Due to the shared testing image used for other scenarios, for simplicity, put
|
||||||
|
relevant python dependencies on `utils/requirement.txt`.
|
||||||
|
|
||||||
|
For OS level dependencies, include your setup in the root folder's `Dockerfile.test`
|
||||||
|
|
||||||
|
## Making new tests
|
||||||
|
|
||||||
|
Create new directory in this folder (`scenario_tests`).
|
||||||
|
Directory should contains:
|
||||||
|
|
||||||
|
- Host level test script called `test.sh`
|
||||||
|
- `docker-compose.yml` file for the service setup
|
||||||
|
- `.env` file if needed for `docker-compose.yml` settings
|
||||||
|
- `tests` directory which contains your test scripts. The testing architecture will
|
||||||
|
execute `test.sh` script in this directory (service level), if you use generic host level `test.sh` script.
|
||||||
|
|
||||||
|
|
||||||
|
Explanations:
|
||||||
|
|
||||||
|
Host level test script is used to setup the docker service, then run the unit test when
|
||||||
|
the service is ready. You can copy paste from existing `collations` for generic script.
|
||||||
|
|
||||||
|
`docker-compose.yml` file should mount your `tests` directory and provides settings
|
||||||
|
needed by the service that are going to be tested.
|
||||||
|
|
||||||
|
`tests` directory contains the actual test script that will be run from *inside*
|
||||||
|
the service. For example, in `collations` scenario `test.sh` (service level scripts)
|
||||||
|
will start python unittest script with necessary variables.
|
||||||
|
|
||||||
|
Add your scenario to travis config:
|
||||||
|
|
||||||
|
In `jobs.include[]` list there will be `stage: test` entry.
|
||||||
|
Add your environment variable needed to run your test in `stage.env` list.
|
||||||
|
For example, if you have new scenario folder `my_test`, then the env key
|
||||||
|
will look like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
- stage: test
|
||||||
|
env:
|
||||||
|
- SCENARIO=replications
|
||||||
|
- SCENARIO=collations
|
||||||
|
- SCENARIO=my_test EXTRA_SETTING_1=value1 EXTRA_SETTING_2=value2 EXTRA_SETTING_3=value3
|
||||||
|
```
|
|
@ -0,0 +1,54 @@
|
||||||
|
|
||||||
|
version: '2.1'
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
pg-data-dir:
|
||||||
|
new-pg-data-dir:
|
||||||
|
|
||||||
|
services:
|
||||||
|
pg:
|
||||||
|
image: 'kartoza/postgis:${TAG:-manual-build}'
|
||||||
|
restart: 'always'
|
||||||
|
# You can optionally mount to volume, to play with the persistence and
|
||||||
|
# observe how the node will behave after restarts.
|
||||||
|
volumes:
|
||||||
|
- pg-data-dir:/var/lib/postgresql
|
||||||
|
- ./tests:/tests
|
||||||
|
- ../utils:/lib/utils
|
||||||
|
environment:
|
||||||
|
DEFAULT_COLLATION: ${DEFAULT_COLLATION:-id_ID.utf8}
|
||||||
|
DEFAULT_CTYPE: ${DEFAULT_COLLATION:-id_ID.utf8}
|
||||||
|
ALLOW_IP_RANGE: '0.0.0.0/0'
|
||||||
|
TEST_CLASS: test_collation.TestCollationDefault
|
||||||
|
ports:
|
||||||
|
- "7777:5432"
|
||||||
|
healthcheck:
|
||||||
|
interval: 60s
|
||||||
|
timeout: 30s
|
||||||
|
retries: 3
|
||||||
|
test: "pg_isready"
|
||||||
|
|
||||||
|
pg-new:
|
||||||
|
image: 'kartoza/postgis:${TAG:-manual-build}'
|
||||||
|
restart: 'always'
|
||||||
|
# You can optionally mount to volume, to play with the persistence and
|
||||||
|
# observe how the node will behave after restarts.
|
||||||
|
volumes:
|
||||||
|
- new-pg-data-dir:/opt/data/postgis
|
||||||
|
- ./tests:/tests
|
||||||
|
- ../utils:/lib/utils
|
||||||
|
hostname: 'pg-new'
|
||||||
|
environment:
|
||||||
|
RECREATE_DATADIR: "True"
|
||||||
|
PGDATA: /opt/data/postgis
|
||||||
|
DEFAULT_COLLATION: ${DEFAULT_COLLATION:-id_ID.utf8}
|
||||||
|
DEFAULT_CTYPE: ${DEFAULT_COLLATION:-id_ID.utf8}
|
||||||
|
ALLOW_IP_RANGE: '0.0.0.0/0'
|
||||||
|
TEST_CLASS: test_collation.TestCollationInitialization
|
||||||
|
ports:
|
||||||
|
- "7776:5432"
|
||||||
|
healthcheck:
|
||||||
|
interval: 60s
|
||||||
|
timeout: 30s
|
||||||
|
retries: 3
|
||||||
|
test: "pg_isready"
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# exit immediately if test fails
|
||||||
|
set -e
|
||||||
|
|
||||||
|
source ../test-env.sh
|
||||||
|
|
||||||
|
# Run service
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
services=("pg" "pg-new")
|
||||||
|
|
||||||
|
for service in "${services[@]}"; do
|
||||||
|
|
||||||
|
# Execute tests
|
||||||
|
until docker-compose exec $service pg_isready; do
|
||||||
|
sleep 1
|
||||||
|
done;
|
||||||
|
docker-compose exec $service /bin/bash /tests/test.sh
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
docker-compose down -v
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
source /env-data.sh
|
||||||
|
|
||||||
|
# execute tests
|
||||||
|
pushd /tests
|
||||||
|
|
||||||
|
cat << EOF
|
||||||
|
Settings used:
|
||||||
|
|
||||||
|
DEFAULT_COLLATION: ${DEFAULT_COLLATION}
|
||||||
|
DEFAULT_CTYPE: ${DEFAULT_CTYPE}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
PGHOST=localhost \
|
||||||
|
PGDATABASE=gis \
|
||||||
|
PYTHONPATH=/lib \
|
||||||
|
python3 -m unittest -v ${TEST_CLASS}
|
|
@ -0,0 +1,95 @@
|
||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
from utils.utils import DBConnection
|
||||||
|
|
||||||
|
|
||||||
|
class TestCollationBase(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.db = DBConnection()
|
||||||
|
|
||||||
|
def fetch_collation(self, cursor, dbname):
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
select datcollate, datctype from pg_database where datname = '{}';
|
||||||
|
""".format(dbname)
|
||||||
|
)
|
||||||
|
|
||||||
|
row = cursor.fetchone()
|
||||||
|
return row
|
||||||
|
|
||||||
|
|
||||||
|
class TestCollationDefault(TestCollationBase):
|
||||||
|
|
||||||
|
def test_check_collation(self):
|
||||||
|
# create new table
|
||||||
|
self.db.conn.autocommit = True
|
||||||
|
with self.db.cursor() as c:
|
||||||
|
|
||||||
|
# Specified database created by entrypoint script should have
|
||||||
|
# the correct collation from database clusters
|
||||||
|
# DEFAULT_COLLATION and DEFAULT_CTYPE will be ignored
|
||||||
|
dbcollate, dbctype = self.fetch_collation(c, 'gis')
|
||||||
|
self.assertEqual(dbcollate, 'C.UTF-8')
|
||||||
|
self.assertEqual(dbctype, 'C.UTF-8')
|
||||||
|
|
||||||
|
c.execute(
|
||||||
|
"""
|
||||||
|
drop database if exists sample_db;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
c.execute(
|
||||||
|
"""
|
||||||
|
create database sample_db;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
# Database created manually will have default settings
|
||||||
|
dbcollate, dbctype = self.fetch_collation(c, 'sample_db')
|
||||||
|
self.assertEqual(dbcollate, 'C.UTF-8')
|
||||||
|
self.assertEqual(dbctype, 'C.UTF-8')
|
||||||
|
|
||||||
|
# Default database created by entrypoint script have
|
||||||
|
# default collation
|
||||||
|
dbcollate, dbctype = self.fetch_collation(c, 'postgres')
|
||||||
|
self.assertEqual(dbcollate, 'C.UTF-8')
|
||||||
|
self.assertEqual(dbctype, 'C.UTF-8')
|
||||||
|
|
||||||
|
|
||||||
|
class TestCollationInitialization(TestCollationBase):
|
||||||
|
|
||||||
|
def test_check_collation_in_new_datadir(self):
|
||||||
|
# create new table
|
||||||
|
default_collation = os.environ.get('DEFAULT_COLLATION')
|
||||||
|
default_ctype = os.environ.get('DEFAULT_CTYPE')
|
||||||
|
self.db.conn.autocommit = True
|
||||||
|
with self.db.cursor() as c:
|
||||||
|
|
||||||
|
# Specified database created by entrypoint script should have
|
||||||
|
# the correct collation from database clusters
|
||||||
|
# DEFAULT_COLLATION and DEFAULT_CTYPE will be ignored
|
||||||
|
dbcollate, dbctype = self.fetch_collation(c, 'gis')
|
||||||
|
self.assertEqual(dbcollate, default_collation)
|
||||||
|
self.assertEqual(dbctype, default_ctype)
|
||||||
|
|
||||||
|
c.execute(
|
||||||
|
"""
|
||||||
|
drop database if exists sample_db;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
c.execute(
|
||||||
|
"""
|
||||||
|
create database sample_db;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
# Database created manually will have default settings
|
||||||
|
dbcollate, dbctype = self.fetch_collation(c, 'sample_db')
|
||||||
|
self.assertEqual(dbcollate, default_collation)
|
||||||
|
self.assertEqual(dbctype, default_ctype)
|
||||||
|
|
||||||
|
# Default database created by entrypoint script have
|
||||||
|
# default collation
|
||||||
|
dbcollate, dbctype = self.fetch_collation(c, 'postgres')
|
||||||
|
self.assertEqual(dbcollate, default_collation)
|
||||||
|
self.assertEqual(dbctype, default_ctype)
|
|
@ -0,0 +1,57 @@
|
||||||
|
version: '2.1'
|
||||||
|
volumes:
|
||||||
|
default-pg-data-dir:
|
||||||
|
new-pg-data-dir:
|
||||||
|
recreate-pg-data-dir:
|
||||||
|
services:
|
||||||
|
pg-default:
|
||||||
|
image: 'kartoza/postgis:${TAG:-manual-build}'
|
||||||
|
volumes:
|
||||||
|
# By default persisted volumes should be in /var/lib/postgresql
|
||||||
|
- default-pg-data-dir:/var/lib/postgresql
|
||||||
|
- ./tests:/tests
|
||||||
|
- ../utils:/lib/utils
|
||||||
|
environment:
|
||||||
|
# Default usage, no datadir location defined
|
||||||
|
TEST_CLASS: TestDefault
|
||||||
|
healthcheck:
|
||||||
|
interval: 60s
|
||||||
|
timeout: 30s
|
||||||
|
retries: 3
|
||||||
|
test: "pg_isready"
|
||||||
|
|
||||||
|
pg-new:
|
||||||
|
image: 'kartoza/postgis:${TAG:-manual-build}'
|
||||||
|
volumes:
|
||||||
|
# Mount to new locations where there are no initial data
|
||||||
|
- new-pg-data-dir:/opt/mypostgis/data
|
||||||
|
- ./tests:/tests
|
||||||
|
- ../utils:/lib/utils
|
||||||
|
environment:
|
||||||
|
# Tell the new location
|
||||||
|
TEST_CLASS: TestNew
|
||||||
|
DATADIR: /opt/mypostgis/data
|
||||||
|
healthcheck:
|
||||||
|
interval: 60s
|
||||||
|
timeout: 30s
|
||||||
|
retries: 3
|
||||||
|
test: "pg_isready"
|
||||||
|
|
||||||
|
pg-recreate:
|
||||||
|
image: 'kartoza/postgis:${TAG:-manual-build}'
|
||||||
|
volumes:
|
||||||
|
- recreate-pg-data-dir:/var/lib/postgresql
|
||||||
|
- ./tests:/tests
|
||||||
|
- ../utils:/lib/utils
|
||||||
|
environment:
|
||||||
|
# Tell that you are going to perform cluster reinitialization
|
||||||
|
TEST_CLASS: TestRecreate
|
||||||
|
RECREATE_DATADIR: "True"
|
||||||
|
DEFAULT_ENCODING: ${DEFAULT_ENCODING:-UTF-8}
|
||||||
|
DEFAULT_COLLATION: ${DEFAULT_COLLATION:-id_ID.utf8}
|
||||||
|
DEFAULT_CTYPE: ${DEFAULT_COLLATION:-id_ID.utf8}
|
||||||
|
healthcheck:
|
||||||
|
interval: 60s
|
||||||
|
timeout: 30s
|
||||||
|
retries: 3
|
||||||
|
test: "pg_isready"
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# exit immediately if test fails
|
||||||
|
set -e
|
||||||
|
|
||||||
|
source ../test-env.sh
|
||||||
|
|
||||||
|
# Run service
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
services=("pg-default" "pg-new" "pg-recreate")
|
||||||
|
|
||||||
|
for service in "${services[@]}"; do
|
||||||
|
|
||||||
|
# Execute tests
|
||||||
|
until docker-compose exec $service pg_isready; do
|
||||||
|
sleep 1
|
||||||
|
done;
|
||||||
|
docker-compose exec $service /bin/bash /tests/test.sh
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
docker-compose down -v
|
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
source /env-data.sh
|
||||||
|
|
||||||
|
# execute tests
|
||||||
|
pushd /tests
|
||||||
|
|
||||||
|
cat << EOF
|
||||||
|
Settings used:
|
||||||
|
|
||||||
|
RECREATE_DATADIR: ${RECREATE_DATADIR}
|
||||||
|
DATADIR: ${DATADIR}
|
||||||
|
PGDATA: ${PGDATA}
|
||||||
|
INITDB_EXTRA_ARGS: ${INITDB_EXTRA_ARGS}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
PGHOST=localhost \
|
||||||
|
PGDATABASE=gis \
|
||||||
|
PYTHONPATH=/lib \
|
||||||
|
python3 -m unittest -v test_datadir.${TEST_CLASS}
|
|
@ -0,0 +1,80 @@
|
||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
from utils.utils import DBConnection
|
||||||
|
|
||||||
|
|
||||||
|
class TestCollationBase(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.db = DBConnection()
|
||||||
|
|
||||||
|
def fetch_collation(self, cursor, dbname):
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
select datcollate, datctype from pg_database where datname = '{}';
|
||||||
|
""".format(dbname)
|
||||||
|
)
|
||||||
|
|
||||||
|
row = cursor.fetchone()
|
||||||
|
return row
|
||||||
|
|
||||||
|
def fetch_datadir_location(self, cursor):
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
show data_directory;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
row = cursor.fetchone()
|
||||||
|
return row[0]
|
||||||
|
|
||||||
|
|
||||||
|
class TestDefault(TestCollationBase):
|
||||||
|
|
||||||
|
def test_check_collation(self):
|
||||||
|
# create new table
|
||||||
|
self.db.conn.autocommit = True
|
||||||
|
with self.db.cursor() as c:
|
||||||
|
|
||||||
|
# Check datadir locations
|
||||||
|
self.assertTrue(
|
||||||
|
self.fetch_datadir_location(c).startswith(
|
||||||
|
'/var/lib/postgresql'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestNew(TestCollationBase):
|
||||||
|
|
||||||
|
def test_check_collation_in_new_datadir(self):
|
||||||
|
# create new table
|
||||||
|
self.db.conn.autocommit = True
|
||||||
|
with self.db.cursor() as c:
|
||||||
|
|
||||||
|
# Check datadir locations
|
||||||
|
self.assertTrue(
|
||||||
|
self.fetch_datadir_location(c).startswith(
|
||||||
|
os.environ.get('DATADIR')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestRecreate(TestCollationBase):
|
||||||
|
|
||||||
|
def test_check_collation_in_new_datadir(self):
|
||||||
|
# create new table
|
||||||
|
self.db.conn.autocommit = True
|
||||||
|
with self.db.cursor() as c:
|
||||||
|
|
||||||
|
# Check datadir locations
|
||||||
|
self.assertTrue(
|
||||||
|
self.fetch_datadir_location(c).startswith(
|
||||||
|
'/var/lib/postgresql'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check that the new cluster is not the default cluster
|
||||||
|
# from it's collations
|
||||||
|
dbcollate, dbctype = self.fetch_collation(c, 'gis')
|
||||||
|
self.assertEqual(dbcollate, os.environ.get('DEFAULT_COLLATION'))
|
||||||
|
self.assertEqual(dbctype, os.environ.get('DEFAULT_CTYPE'))
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
|
||||||
|
version: '2.1'
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
pg-master-data-dir:
|
||||||
|
pg-node-data-dir:
|
||||||
|
|
||||||
|
services:
|
||||||
|
pg-master:
|
||||||
|
image: 'kartoza/postgis:${TAG:-manual-build}'
|
||||||
|
restart: 'always'
|
||||||
|
# You can optionally mount to volume, to play with the persistence and
|
||||||
|
# observe how the node will behave after restarts.
|
||||||
|
volumes:
|
||||||
|
- pg-master-data-dir:/var/lib/postgresql
|
||||||
|
- ./tests:/tests
|
||||||
|
- ../utils:/lib/utils
|
||||||
|
environment:
|
||||||
|
# ALLOW_IP_RANGE option is used to specify additionals allowed domains
|
||||||
|
# in pg_hba.
|
||||||
|
# This range should allow nodes to connect to master
|
||||||
|
ALLOW_IP_RANGE: '0.0.0.0/0'
|
||||||
|
|
||||||
|
# We can specify optional credentials
|
||||||
|
REPLICATION_USER: 'replicator'
|
||||||
|
REPLICATION_PASS: 'replicator'
|
||||||
|
# Setup master replication variables
|
||||||
|
#PG_MAX_WAL_SENDERS: 8
|
||||||
|
#PG_WAL_KEEP_SEGMENTS: 100
|
||||||
|
# You can expose the port to observe it in your local machine
|
||||||
|
ports:
|
||||||
|
- "7777:5432"
|
||||||
|
healthcheck:
|
||||||
|
interval: 60s
|
||||||
|
timeout: 30s
|
||||||
|
retries: 3
|
||||||
|
test: "pg_isready"
|
||||||
|
|
||||||
|
pg-node:
|
||||||
|
image: 'kartoza/postgis:${TAG:-manual-build}'
|
||||||
|
restart: 'always'
|
||||||
|
# You can optionally mount to volume, but we're not able to scale it
|
||||||
|
# in that case.
|
||||||
|
# The node will always destroy its database and copy from master at
|
||||||
|
# runtime
|
||||||
|
volumes:
|
||||||
|
- pg-node-data-dir:/var/lib/postgresql
|
||||||
|
- ./tests:/tests
|
||||||
|
- ../utils:/lib/utils
|
||||||
|
|
||||||
|
environment:
|
||||||
|
# ALLOW_IP_RANGE option is used to specify additionals allowed domains
|
||||||
|
# in pg_hba.
|
||||||
|
# Not really needed in nodes for the replication, but optionally can
|
||||||
|
# be put when nodes are needed to be a failover server when master
|
||||||
|
# is down. The IP Range are generally needed if other services wants to
|
||||||
|
# connect to this node
|
||||||
|
ALLOW_IP_RANGE: '0.0.0.0/0'
|
||||||
|
|
||||||
|
# REPLICATE_FROM options accepts domain-name or IP address
|
||||||
|
# with this in mind, you can also put docker service name, because it
|
||||||
|
# will be resolved as host name.
|
||||||
|
REPLICATE_FROM: 'pg-master'
|
||||||
|
|
||||||
|
# REPLICATE_PORT will default to 5432 if not specified.
|
||||||
|
# REPLICATE_PORT: '5432'
|
||||||
|
# In the case where you need to replicate from outside service,
|
||||||
|
# you can put the server address and port here, as long as the target
|
||||||
|
# where configured as master, and replicable.
|
||||||
|
# REPLICATE_FROM: '192.168.1.8'
|
||||||
|
# REPLICATE_PORT: '7777'
|
||||||
|
|
||||||
|
# DESTROY_DATABASE_ON_RESTART will default to True if not specified.
|
||||||
|
# If specified other than True, it will prevent node from destroying
|
||||||
|
# database on restart
|
||||||
|
DESTROY_DATABASE_ON_RESTART: 'True'
|
||||||
|
|
||||||
|
# PROMOTE_MASTER Default empty.
|
||||||
|
# If specified with any value, then it will convert current node into
|
||||||
|
# a writable state. Useful if master is down and the current node needs
|
||||||
|
# to be promoted until manual recovery.
|
||||||
|
# PROMOTE_MASTER: 'True'
|
||||||
|
|
||||||
|
# For now we don't support different credentials for replication
|
||||||
|
# so we use the same credentials as master's superuser, or anything that
|
||||||
|
# have replication role.
|
||||||
|
REPLICATION_USER: 'replicator'
|
||||||
|
REPLICATION_PASS: 'replicator'
|
||||||
|
depends_on:
|
||||||
|
pg-master:
|
||||||
|
condition: service_healthy
|
||||||
|
# You can expose the port to observe it in your local machine
|
||||||
|
# For this sample, it was disabled by default to allow scaling test
|
||||||
|
ports:
|
||||||
|
- "7776:5432"
|
||||||
|
healthcheck:
|
||||||
|
interval: 60s
|
||||||
|
timeout: 30s
|
||||||
|
retries: 3
|
||||||
|
test: "pg_isready"
|
|
@ -0,0 +1,29 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# exit immediately if test fails
|
||||||
|
set -e
|
||||||
|
|
||||||
|
source ../test-env.sh
|
||||||
|
|
||||||
|
# Run service
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
# Preparing master cluster
|
||||||
|
until docker-compose exec pg-master pg_isready; do
|
||||||
|
sleep 1
|
||||||
|
done;
|
||||||
|
|
||||||
|
# Execute tests
|
||||||
|
docker-compose exec pg-master /bin/bash /tests/test_master.sh
|
||||||
|
|
||||||
|
# Preparing node cluster
|
||||||
|
until docker-compose exec pg-node pg_isready; do
|
||||||
|
sleep 1
|
||||||
|
done;
|
||||||
|
|
||||||
|
# Execute tests
|
||||||
|
docker-compose exec pg-node /bin/bash /tests/test_node.sh
|
||||||
|
|
||||||
|
docker-compose down -v
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
source /env-data.sh
|
||||||
|
|
||||||
|
# execute tests
|
||||||
|
pushd /tests
|
||||||
|
|
||||||
|
PGHOST=localhost \
|
||||||
|
PGDATABASE=gis \
|
||||||
|
PYTHONPATH=/lib \
|
||||||
|
python3 -m unittest -v test_replication.TestReplicationMaster
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
source /env-data.sh
|
||||||
|
|
||||||
|
# execute tests
|
||||||
|
pushd /tests
|
||||||
|
|
||||||
|
PGHOST=localhost \
|
||||||
|
PGDATABASE=gis \
|
||||||
|
PYTHONPATH=/lib \
|
||||||
|
python3 -m unittest -v test_replication.TestReplicationNode
|
|
@ -0,0 +1,59 @@
|
||||||
|
import unittest
|
||||||
|
from utils.utils import DBConnection
|
||||||
|
|
||||||
|
|
||||||
|
class TestReplicationMaster(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.db = DBConnection()
|
||||||
|
|
||||||
|
def test_create_new_data(self):
|
||||||
|
# create new table
|
||||||
|
self.db.conn.autocommit = True
|
||||||
|
with self.db.cursor() as c:
|
||||||
|
c.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE IF NOT EXISTS test_replication_table (
|
||||||
|
id integer not null
|
||||||
|
constraint pkey primary key,
|
||||||
|
geom geometry(Point, 4326),
|
||||||
|
name varchar(30),
|
||||||
|
alias varchar(30),
|
||||||
|
description varchar(255)
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
c.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO test_replication_table (id, geom, name, alias, description)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
1,
|
||||||
|
st_setsrid(st_point(107.6097, 6.9120), 4326),
|
||||||
|
'Bandung',
|
||||||
|
'Paris van Java',
|
||||||
|
'Asia-Africa conference was held here'
|
||||||
|
) ON CONFLICT DO NOTHING;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestReplicationNode(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.db = DBConnection()
|
||||||
|
|
||||||
|
def test_read_data(self):
|
||||||
|
# create new table
|
||||||
|
self.db.conn.autocommit = True
|
||||||
|
with self.db.cursor() as c:
|
||||||
|
c.execute(
|
||||||
|
"""
|
||||||
|
SELECT * FROM test_replication_table;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
rows = c.fetchall()
|
||||||
|
self.assertEqual(len(rows), 1)
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Display test environment variable
|
||||||
|
|
||||||
|
cat << EOF
|
||||||
|
Test environment:
|
||||||
|
|
||||||
|
Compose Project : ${COMPOSE_PROJECT_NAME}
|
||||||
|
Compose File : ${COMPOSE_PROJECT_FILE}
|
||||||
|
Image tag : ${TAG}
|
||||||
|
|
||||||
|
EOF
|
|
@ -0,0 +1 @@
|
||||||
|
psycopg2-binary
|
|
@ -0,0 +1,43 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
import psycopg2
|
||||||
|
|
||||||
|
|
||||||
|
class DBConnection:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.conn = DBConnection.create_conn()
|
||||||
|
|
||||||
|
def table_exists(self, table_name, table_schema='public'):
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
query = (
|
||||||
|
'select '
|
||||||
|
'exists('
|
||||||
|
'select 1 '
|
||||||
|
'from information_schema.tables '
|
||||||
|
'where table_name = %s and table_schema = %s)')
|
||||||
|
cur.execute(query, (table_name, table_schema))
|
||||||
|
try:
|
||||||
|
row = cur.fetchone()
|
||||||
|
return row[0]
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_conn():
|
||||||
|
"""
|
||||||
|
:return: psycopg2.connection
|
||||||
|
"""
|
||||||
|
return psycopg2.connect(
|
||||||
|
host=os.environ.get('POSTGRES_HOST'),
|
||||||
|
database=os.environ.get('POSTGRES_DB'),
|
||||||
|
user=os.environ.get('POSTGRES_USER'),
|
||||||
|
password=os.environ.get('POSTGRES_PASS'),
|
||||||
|
port=os.environ.get('POSTGRES_PORT')
|
||||||
|
)
|
||||||
|
|
||||||
|
def cursor(self):
|
||||||
|
"""
|
||||||
|
:return: psycopg2.cursor
|
||||||
|
"""
|
||||||
|
return self.conn.cursor()
|
|
@ -15,6 +15,11 @@ SINGLE_DB=${arr[0]}
|
||||||
# Refresh configuration in case environment settings changed.
|
# Refresh configuration in case environment settings changed.
|
||||||
cat $CONF.template > $CONF
|
cat $CONF.template > $CONF
|
||||||
|
|
||||||
|
# Reflect DATADIR loaction
|
||||||
|
# Delete any data_dir declarations
|
||||||
|
sed -i '/data_directory/d' $CONF
|
||||||
|
echo "data_directory = '${DATADIR}'" >> $CONF
|
||||||
|
|
||||||
# This script will setup necessary configuration to optimise for PostGIS and to enable replications
|
# This script will setup necessary configuration to optimise for PostGIS and to enable replications
|
||||||
cat >> $CONF <<EOF
|
cat >> $CONF <<EOF
|
||||||
archive_mode = ${ARCHIVE_MODE}
|
archive_mode = ${ARCHIVE_MODE}
|
||||||
|
|
|
@ -2,38 +2,35 @@
|
||||||
|
|
||||||
source /env-data.sh
|
source /env-data.sh
|
||||||
|
|
||||||
|
|
||||||
SETUP_LOCKFILE="${DATADIR}/.postgresql.init.lock"
|
|
||||||
|
|
||||||
# This script will setup the necessary folder for database
|
|
||||||
chown -R postgres /var/lib/postgresql
|
|
||||||
# test if DATADIR has content
|
# test if DATADIR has content
|
||||||
if [[ -z "${EXISTING_DATA_DIR}" ]]; then \
|
# Do initialization if DATADIR is empty, or RECREATE_DATADIR is true
|
||||||
if [[ ! -f "${SETUP_LOCKFILE}" ]]; then
|
if [[ -z "$(ls -A ${DATADIR} 2> /dev/null)" || "${RECREATE_DATADIR}" == 'TRUE' ]]; then
|
||||||
# No content yet - first time pg is being run!
|
# Only attempt reinitializations if ${RECREATE_DATADIR} is true
|
||||||
# No Replicate From settings. Assume that this is a master database.
|
# No Replicate From settings. Assume that this is a master database.
|
||||||
# Initialise db
|
# Initialise db
|
||||||
echo "Initializing Postgres Database at ${DATADIR}"
|
echo "Initializing Postgres Database at ${DATADIR}"
|
||||||
rm -rf ${DATADIR}/*
|
mkdir -p ${DATADIR}
|
||||||
chown -R postgres /var/lib/postgresql
|
rm -rf ${DATADIR}/*
|
||||||
su - postgres -c "$INITDB -U postgres -E ${DEFAULT_ENCODING} --lc-collate=${DEFAULT_COLLATION} --lc-ctype=${DEFAULT_CTYPE} --wal-segsize=${WAL_SEGSIZE} -D ${DATADIR}"
|
chown -R postgres:postgres ${DATADIR}
|
||||||
touch ${SETUP_LOCKFILE}
|
echo "Initializing with command:"
|
||||||
fi
|
command="$INITDB -U postgres -E ${DEFAULT_ENCODING} --lc-collate=${DEFAULT_COLLATION} --lc-ctype=${DEFAULT_CTYPE} --wal-segsize=${WAL_SEGSIZE} -D ${DATADIR} ${INITDB_EXTRA_ARGS}"
|
||||||
|
su - postgres -c "$command"
|
||||||
fi;
|
fi;
|
||||||
|
|
||||||
# Set proper permissions
|
# Set proper permissions
|
||||||
# needs to be done as root:
|
# needs to be done as root:
|
||||||
chown -R postgres:postgres ${DATADIR}
|
chown -R postgres:postgres ${DATADIR}
|
||||||
|
chmod -R 750 ${DATADIR}
|
||||||
|
|
||||||
# test database existing
|
# test database existing
|
||||||
trap "echo \"Sending SIGTERM to postgres\"; killall -s SIGTERM postgres" SIGTERM
|
trap "echo \"Sending SIGTERM to postgres\"; killall -s SIGTERM postgres" SIGTERM
|
||||||
|
|
||||||
|
|
||||||
|
# Run as local only for config setup phase to avoid outside access
|
||||||
su - postgres -c "${POSTGRES} -D ${DATADIR} -c config_file=${CONF} ${LOCALONLY} &"
|
su - postgres -c "${POSTGRES} -D ${DATADIR} -c config_file=${CONF} ${LOCALONLY} &"
|
||||||
|
|
||||||
# wait for postgres to come up
|
# wait for postgres to come up
|
||||||
until su - postgres -c "psql -l"; do
|
until su - postgres -c "pg_isready"; do
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
echo "postgres ready"
|
echo "postgres ready"
|
||||||
|
@ -42,7 +39,7 @@ echo "postgres ready"
|
||||||
source /setup-user.sh
|
source /setup-user.sh
|
||||||
|
|
||||||
# enable extensions in template1 if env variable set to true
|
# enable extensions in template1 if env variable set to true
|
||||||
if [ "$POSTGRES_TEMPLATE_EXTENSIONS" = true ] ; then
|
if [[ "$(boolean ${POSTGRES_TEMPLATE_EXTENSIONS})" == TRUE ]] ; then
|
||||||
for ext in $(echo ${POSTGRES_MULTIPLE_EXTENSIONS} | tr ',' ' '); do
|
for ext in $(echo ${POSTGRES_MULTIPLE_EXTENSIONS} | tr ',' ' '); do
|
||||||
echo "Enabling ${ext} in the database template1"
|
echo "Enabling ${ext} in the database template1"
|
||||||
su - postgres -c "psql -c 'CREATE EXTENSION IF NOT EXISTS ${ext} cascade;' template1"
|
su - postgres -c "psql -c 'CREATE EXTENSION IF NOT EXISTS ${ext} cascade;' template1"
|
||||||
|
@ -60,7 +57,7 @@ for db in $(echo ${POSTGRES_DBNAME} | tr ',' ' '); do
|
||||||
RESULT=`su - postgres -c "psql -t -c \"SELECT count(1) from pg_database where datname='${db}';\""`
|
RESULT=`su - postgres -c "psql -t -c \"SELECT count(1) from pg_database where datname='${db}';\""`
|
||||||
if [[ ${RESULT} -eq 0 ]]; then
|
if [[ ${RESULT} -eq 0 ]]; then
|
||||||
echo "Create db ${db}"
|
echo "Create db ${db}"
|
||||||
su - postgres -c "createdb -O ${POSTGRES_USER} ${db}"
|
su - postgres -c "createdb -O ${POSTGRES_USER} ${db}"
|
||||||
for ext in $(echo ${POSTGRES_MULTIPLE_EXTENSIONS} | tr ',' ' '); do
|
for ext in $(echo ${POSTGRES_MULTIPLE_EXTENSIONS} | tr ',' ' '); do
|
||||||
echo "Enabling ${ext} in the database ${db}"
|
echo "Enabling ${ext} in the database ${db}"
|
||||||
if [[ ${ext} = 'pg_cron' ]]; then
|
if [[ ${ext} = 'pg_cron' ]]; then
|
||||||
|
@ -72,8 +69,7 @@ for db in $(echo ${POSTGRES_DBNAME} | tr ',' ' '); do
|
||||||
echo "Loading legacy sql"
|
echo "Loading legacy sql"
|
||||||
su - postgres -c "psql ${db} -f ${SQLDIR}/legacy_minimal.sql" || true
|
su - postgres -c "psql ${db} -f ${SQLDIR}/legacy_minimal.sql" || true
|
||||||
su - postgres -c "psql ${db} -f ${SQLDIR}/legacy_gist.sql" || true
|
su - postgres -c "psql ${db} -f ${SQLDIR}/legacy_gist.sql" || true
|
||||||
export PGPASSWORD=${POSTGRES_PASS}
|
PGPASSWORD=${POSTGRES_PASS} psql ${db} -U ${POSTGRES_USER} -p 5432 -h localhost -f custom.sql
|
||||||
psql ${db} -U ${POSTGRES_USER} -p 5432 -h localhost -f custom.sql
|
|
||||||
|
|
||||||
else
|
else
|
||||||
echo "${db} db already exists"
|
echo "${db} db already exists"
|
||||||
|
@ -87,4 +83,4 @@ fi
|
||||||
|
|
||||||
rm custom.sql
|
rm custom.sql
|
||||||
# This should show up in docker logs afterwards
|
# This should show up in docker logs afterwards
|
||||||
su - postgres -c "psql -l"
|
su - postgres -c "psql -l 2>&1"
|
||||||
|
|
Ładowanie…
Reference in New Issue