@@ -24,153 +24,253 @@ file_env() {
2424 unset " $fileVar "
2525}
2626
27- if [ " ${1: 0: 1} " = ' -' ]; then
28- set -- postgres " $@ "
29- fi
27+ # check to see if this file is being run or sourced from another script
28+ _is_sourced () {
29+ # https://unix.stackexchange.com/a/215279
30+ [ " ${# FUNCNAME[@]} " -ge 2 ] \
31+ && [ " ${FUNCNAME[0]} " = ' _is_sourced' ] \
32+ && [ " ${FUNCNAME[1]} " = ' source' ]
33+ }
34+
35+ # used to create initial posgres directories and if run as root, ensure ownership to the "postgres" user
36+ docker_create_db_directories () {
37+ local user; user=" $( id -u) "
3038
31- # allow the container to be started with `--user`
32- if [ " $1 " = ' postgres' ] && [ " $( id -u) " = ' 0' ]; then
3339 mkdir -p " $PGDATA "
34- chown -R postgres " $PGDATA "
3540 chmod 700 " $PGDATA "
3641
37- mkdir -p /var/run/postgresql
38- chown -R postgres /var/run/postgresql
39- chmod 775 /var/run/postgresql
42+ # ignore failure since it will be fine when using the image provided directory; see also https://github.com/docker-library/postgres/pull/289
43+ mkdir -p /var/run/postgresql || :
44+ chmod 775 /var/run/postgresql || :
4045
41- # Create the transaction log directory before initdb is run (below) so the directory is owned by the correct user
46+ # Create the transaction log directory before initdb is run so the directory is owned by the correct user
4247 if [ " $POSTGRES_INITDB_WALDIR " ]; then
4348 mkdir -p " $POSTGRES_INITDB_WALDIR "
44- chown -R postgres " $POSTGRES_INITDB_WALDIR "
49+ if [ " $user " = ' 0' ]; then
50+ find " $POSTGRES_INITDB_WALDIR " \! -user postgres -exec chown postgres ' {}' +
51+ fi
4552 chmod 700 " $POSTGRES_INITDB_WALDIR "
4653 fi
4754
48- exec su-exec postgres " $BASH_SOURCE " " $@ "
49- fi
50-
51- if [ " $1 " = ' postgres' ]; then
52- mkdir -p " $PGDATA "
53- chown -R " $( id -u) " " $PGDATA " 2> /dev/null || :
54- chmod 700 " $PGDATA " 2> /dev/null || :
55+ # allow the container to be started with `--user`
56+ if [ " $user " = ' 0' ]; then
57+ find " $PGDATA " \! -user postgres -exec chown postgres ' {}' +
58+ find /var/run/postgresql \! -user postgres -exec chown postgres ' {}' +
59+ fi
60+ }
5561
56- # look specifically for PG_VERSION, as it is expected in the DB dir
57- if [ ! -s " $PGDATA /PG_VERSION" ]; then
58- # "initdb" is particular about the current user existing in "/etc/passwd", so we use "nss_wrapper" to fake that if necessary
59- # see https://github.com/docker-library/postgres/pull/253, https://github.com/docker-library/postgres/issues/359, https://cwrap.org/nss_wrapper.html
60- if ! getent passwd " $( id -u) " & > /dev/null && [ -e /usr/lib/libnss_wrapper.so ]; then
61- export LD_PRELOAD=' /usr/lib/libnss_wrapper.so'
62- export NSS_WRAPPER_PASSWD=" $( mktemp) "
63- export NSS_WRAPPER_GROUP=" $( mktemp) "
64- echo " postgres:x:$( id -u) :$( id -g) :PostgreSQL:$PGDATA :/bin/false" > " $NSS_WRAPPER_PASSWD "
65- echo " postgres:x:$( id -g) :" > " $NSS_WRAPPER_GROUP "
66- fi
62+ # initialize empty PGDATA directory with new database via 'initdb'
63+ # arguments to `initdb` can be passed via POSTGRES_INITDB_ARGS or as arguments to this function
64+ # `initdb` automatically creates the "postgres", "template0", and "template1" dbnames
65+ # this is also where the database user is created, specified by `POSTGRES_USER` env
66+ docker_init_database_dir () {
67+ # "initdb" is particular about the current user existing in "/etc/passwd", so we use "nss_wrapper" to fake that if necessary
68+ # see https://github.com/docker-library/postgres/pull/253, https://github.com/docker-library/postgres/issues/359, https://cwrap.org/nss_wrapper.html
69+ if ! getent passwd " $( id -u) " & > /dev/null && [ -e /usr/lib/libnss_wrapper.so ]; then
70+ export LD_PRELOAD=' /usr/lib/libnss_wrapper.so'
71+ export NSS_WRAPPER_PASSWD=" $( mktemp) "
72+ export NSS_WRAPPER_GROUP=" $( mktemp) "
73+ echo " postgres:x:$( id -u) :$( id -g) :PostgreSQL:$PGDATA :/bin/false" > " $NSS_WRAPPER_PASSWD "
74+ echo " postgres:x:$( id -g) :" > " $NSS_WRAPPER_GROUP "
75+ fi
6776
68- file_env ' POSTGRES_USER' ' postgres'
69- file_env ' POSTGRES_PASSWORD'
77+ if [ " $POSTGRES_INITDB_WALDIR " ]; then
78+ set -- --waldir " $POSTGRES_INITDB_WALDIR " " $@ "
79+ fi
7080
71- file_env ' POSTGRES_INITDB_ARGS'
72- if [ " $POSTGRES_INITDB_WALDIR " ]; then
73- export POSTGRES_INITDB_ARGS=" $POSTGRES_INITDB_ARGS --waldir $POSTGRES_INITDB_WALDIR "
74- fi
75- eval ' initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") ' " $POSTGRES_INITDB_ARGS "
81+ eval ' initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") ' " $POSTGRES_INITDB_ARGS " ' "$@"'
7682
77- # unset/cleanup "nss_wrapper" bits
78- if [ " ${LD_PRELOAD:- } " = ' /usr/lib/libnss_wrapper.so' ]; then
79- rm -f " $NSS_WRAPPER_PASSWD " " $NSS_WRAPPER_GROUP "
80- unset LD_PRELOAD NSS_WRAPPER_PASSWD NSS_WRAPPER_GROUP
81- fi
83+ # unset/cleanup "nss_wrapper" bits
84+ if [ " ${LD_PRELOAD:- } " = ' /usr/lib/libnss_wrapper.so' ]; then
85+ rm -f " $NSS_WRAPPER_PASSWD " " $NSS_WRAPPER_GROUP "
86+ unset LD_PRELOAD NSS_WRAPPER_PASSWD NSS_WRAPPER_GROUP
87+ fi
88+ }
8289
83- # check password first so we can output the warning before postgres
84- # messes it up
85- if [ -n " $POSTGRES_PASSWORD " ]; then
86- authMethod=md5
90+ # print large warning if POSTGRES_PASSWORD is empty
91+ docker_verify_minimum_env () {
92+ # check password first so we can output the warning before postgres
93+ # messes it up
94+ if [ " ${# POSTGRES_PASSWORD} " -ge 100 ]; then
95+ cat >&2 << -'EOWARN '
8796
88- if [ " ${# POSTGRES_PASSWORD} " -ge 100 ]; then
89- cat >&2 << -'EOWARN '
97+ WARNING: The supplied POSTGRES_PASSWORD is 100+ characters.
9098
91- WARNING: The supplied POSTGRES_PASSWORD is 100+ characters .
99+ This will not work if used via PGPASSWORD with "psql" .
92100
93- This will not work if used via PGPASSWORD with "psql".
101+ https://www.postgresql.org/message-id/flat/E1Rqxp2-0004Qt-PL%40wrigleys.postgresql.org (BUG #6412)
102+ https://github.com/docker-library/postgres/issues/507
94103
95- https://www.postgresql.org/message-id/flat/E1Rqxp2-0004Qt-PL%40wrigleys.postgresql.org (BUG #6412)
96- https://github.com/docker-library/postgres/issues/507
104+ EOWARN
105+ fi
106+ if [ -z " $POSTGRES_PASSWORD " ]; then
107+ # The - option suppresses leading tabs but *not* spaces. :)
108+ cat >&2 << -'EOWARN '
109+ ****************************************************
110+ WARNING: No password has been set for the database.
111+ This will allow anyone with access to the
112+ Postgres port to access your database. In
113+ Docker's default configuration, this is
114+ effectively any other container on the same
115+ system.
97116
98- EOWARN
99- fi
100- else
101- # The - option suppresses leading tabs but *not* spaces. :)
102- cat >&2 << -'EOWARN '
103- ****************************************************
104- WARNING: No password has been set for the database.
105- This will allow anyone with access to the
106- Postgres port to access your database. In
107- Docker's default configuration, this is
108- effectively any other container on the same
109- system.
110-
111- Use "-e POSTGRES_PASSWORD=password" to set
112- it in "docker run".
113- ****************************************************
114- EOWARN
115-
116- authMethod=trust
117- fi
117+ Use "-e POSTGRES_PASSWORD=password" to set
118+ it in "docker run".
119+ ****************************************************
120+ EOWARN
118121
119- {
120- echo
121- echo " host all all all $authMethod "
122- } >> " $PGDATA /pg_hba.conf"
122+ fi
123+ }
123124
124- # internal start of server in order to allow set-up using psql-client
125- # does not listen on external TCP/IP and waits until start finishes
126- PGUSER= " ${PGUSER :- $POSTGRES_USER } " \
127- pg_ctl -D " $PGDATA " \
128- -o " -c listen_addresses='' " \
129- -w start
125+ # usage: docker_process_init_files [file [file [...]]]
126+ # ie: docker_process_init_files /always-initdb.d/*
127+ # process initializer files, based on file extensions and permissions
128+ docker_process_init_files () {
129+ # psql here for backwards compatiblilty "${psql[@]}"
130+ psql=( docker_process_sql )
130131
131- file_env ' POSTGRES_DB' " $POSTGRES_USER "
132+ echo
133+ local f
134+ for f; do
135+ case " $f " in
136+ * .sh)
137+ # https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
138+ # https://github.com/docker-library/postgres/pull/452
139+ if [ -x " $f " ]; then
140+ echo " $0 : running $f "
141+ " $f "
142+ else
143+ echo " $0 : sourcing $f "
144+ . " $f "
145+ fi
146+ ;;
147+ * .sql) echo " $0 : running $f " ; docker_process_sql -f " $f " ; echo ;;
148+ * .sql.gz) echo " $0 : running $f " ; gunzip -c " $f " | docker_process_sql; echo ;;
149+ * ) echo " $0 : ignoring $f " ;;
150+ esac
151+ echo
152+ done
153+ }
132154
133- export PGPASSWORD=" ${PGPASSWORD:- $POSTGRES_PASSWORD } "
134- psql=( psql -v ON_ERROR_STOP=1 --username " $POSTGRES_USER " --no-password )
155+ # Execute sql script, passed via stdin (or -f flag of pqsl)
156+ # usage: docker_process_sql [psql-cli-args]
157+ # ie: docker_process_sql --dbname=mydb <<<'INSERT ...'
158+ # ie: docker_process_sql -f my-file.sql
159+ # ie: docker_process_sql <my-file.sql
160+ docker_process_sql () {
161+ local query_runner=( psql -v ON_ERROR_STOP=1 --username " $POSTGRES_USER " --no-password )
162+ if [ -n " $POSTGRES_DB " ]; then
163+ query_runner+=( --dbname " $POSTGRES_DB " )
164+ fi
135165
136- if [ " $POSTGRES_DB " != ' postgres' ]; then
137- " ${psql[@]} " --dbname postgres --set db=" $POSTGRES_DB " << -'EOSQL '
138- CREATE DATABASE :"db" ;
139- EOSQL
140- echo
141- fi
142- psql+=( --dbname " $POSTGRES_DB " )
166+ " ${query_runner[@]} " " $@ "
167+ }
143168
169+ # create initial database
170+ # uses environment variables for input: POSTGRES_DB
171+ docker_setup_db () {
172+ if [ " $POSTGRES_DB " != ' postgres' ]; then
173+ POSTGRES_DB= docker_process_sql --dbname postgres --set db=" $POSTGRES_DB " << -'EOSQL '
174+ CREATE DATABASE :"db" ;
175+ EOSQL
144176 echo
145- for f in /docker-entrypoint-initdb.d/* ; do
146- case " $f " in
147- * .sh)
148- # https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
149- # https://github.com/docker-library/postgres/pull/452
150- if [ -x " $f " ]; then
151- echo " $0 : running $f "
152- " $f "
153- else
154- echo " $0 : sourcing $f "
155- . " $f "
156- fi
157- ;;
158- * .sql) echo " $0 : running $f " ; " ${psql[@]} " -f " $f " ; echo ;;
159- * .sql.gz) echo " $0 : running $f " ; gunzip -c " $f " | " ${psql[@]} " ; echo ;;
160- * ) echo " $0 : ignoring $f " ;;
161- esac
162- echo
163- done
177+ fi
178+ }
164179
165- PGUSER=" ${PGUSER:- $POSTGRES_USER } " \
166- pg_ctl -D " $PGDATA " -m fast -w stop
180+ # Loads various settings that are used elsewhere in the script
181+ # This should be called before any other functions
182+ docker_setup_env () {
183+ file_env ' POSTGRES_PASSWORD'
167184
168- unset PGPASSWORD
185+ file_env ' POSTGRES_USER' ' postgres'
186+ file_env ' POSTGRES_DB' " $POSTGRES_USER "
187+ file_env ' POSTGRES_INITDB_ARGS'
169188
189+ declare -g DATABASE_ALREADY_EXISTS
190+ # look specifically for PG_VERSION, as it is expected in the DB dir
191+ if [ -s " $PGDATA /PG_VERSION" ]; then
192+ DATABASE_ALREADY_EXISTS=' true'
193+ fi
194+ }
195+
196+ # append md5 or trust auth to pg_hba.conf based on existence of POSTGRES_PASSWORD
197+ pg_setup_hba_conf () {
198+ local authMethod=' md5'
199+ if [ -z " $POSTGRES_PASSWORD " ]; then
200+ authMethod=' trust'
201+ fi
202+
203+ {
170204 echo
171- echo ' PostgreSQL init process complete; ready for start up.'
172- echo
205+ echo " host all all all $authMethod "
206+ } >> " $PGDATA /pg_hba.conf"
207+ }
208+
209+ # start socket-only postgresql server for setting up or running scripts
210+ # all arguments will be passed along as arguments to `postgres` (via pg_ctl)
211+ docker_temp_server_start () {
212+ if [ " $1 " = ' postgres' ]; then
213+ shift
173214 fi
174- fi
215+ # internal start of server in order to allow setup using psql client
216+ # does not listen on external TCP/IP and waits until start finishes (can be overridden via args)
217+ PGUSER=" ${PGUSER:- $POSTGRES_USER } " \
218+ pg_ctl -D " $PGDATA " \
219+ -o " -c listen_addresses='' $( [ " $# " -gt 0 ] && printf ' %q ' " $@ " ) " \
220+ -w start
221+ }
222+
223+ # stop postgresql server after done setting up user and running scripts
224+ docker_temp_server_stop () {
225+ PGUSER=" ${PGUSER:- postgres} " \
226+ pg_ctl -D " $PGDATA " -m fast -w stop
227+ }
228+
229+ _main () {
230+ # if first arg looks like a flag, assume we want to run postgres server
231+ if [ " ${1: 0: 1} " = ' -' ]; then
232+ set -- postgres " $@ "
233+ fi
234+
235+ if [ " $1 " = ' postgres' ]; then
236+ docker_setup_env
237+ # setup data directories and permissions (when run as root)
238+ docker_create_db_directories
239+ if [ " $( id -u) " = ' 0' ]; then
240+ # then restart script as postgres user
241+ exec su-exec postgres " $BASH_SOURCE " " $@ "
242+ fi
243+
244+ # only run initialization on an empty data directory
245+ if [ -z " $DATABASE_ALREADY_EXISTS " ]; then
246+ docker_verify_minimum_env
247+ docker_init_database_dir
248+ pg_setup_hba_conf
249+
250+ # PGPASSWORD is required for psql when authentication is required for 'local' connections via pg_hba.conf and is otherwise harmless
251+ # e.g. when '--auth=md5' or '--auth-local=md5' is used in POSTGRES_INITDB_ARGS
252+ export PGPASSWORD=" ${PGPASSWORD:- $POSTGRES_PASSWORD } "
253+ docker_temp_server_start " $@ "
254+
255+ docker_setup_db
256+ docker_process_init_files /docker-entrypoint-initdb.d/*
257+
258+ docker_temp_server_stop
259+ unset PGPASSWORD
260+
261+ echo
262+ echo ' PostgreSQL init process complete; ready for start up.'
263+ echo
264+ else
265+ echo
266+ echo ' PostgreSQL Database directory appears to contain a database; Skipping initialization'
267+ echo
268+ fi
269+ fi
270+
271+ exec " $@ "
272+ }
175273
176- exec " $@ "
274+ if ! _is_sourced; then
275+ _main " $@ "
276+ fi
0 commit comments