@@ -24,153 +24,254 @@ 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=" $( 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+ [ " $user " = ' 0 ' ] && find " $POSTGRES_INITDB_WALDIR " \! -user postgres - exec chown postgres ' {} ' +
4550 chmod 700 " $POSTGRES_INITDB_WALDIR "
4651 fi
4752
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 || :
53+ # allow the container to be started with `--user`
54+ if [ " $user " = ' 0' ]; then
55+ find " $PGDATA " \! -user postgres -exec chown postgres ' {}' +
56+ find /var/run/postgresql \! -user postgres -exec chown postgres ' {}' +
57+ fi
58+ }
5559
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
60+ # initialize empty PGDATA directory with new database via 'initdb'
61+ # arguments to `initdb` can be passed via POSTGRES_INITDB_ARGS or as arguments to this function
62+ # `initdb` automatically creates the "postgres", "template0", and "template1" dbnames
63+ # this is also where the database user is created, specified by `POSTGRES_USER` env
64+ docker_init_database_dir () {
65+ # "initdb" is particular about the current user existing in "/etc/passwd", so we use "nss_wrapper" to fake that if necessary
66+ # see https://github.com/docker-library/postgres/pull/253, https://github.com/docker-library/postgres/issues/359, https://cwrap.org/nss_wrapper.html
67+ if ! getent passwd " $( id -u) " & > /dev/null && [ -e /usr/lib/libnss_wrapper.so ]; then
68+ export LD_PRELOAD=' /usr/lib/libnss_wrapper.so'
69+ export NSS_WRAPPER_PASSWD=" $( mktemp) "
70+ export NSS_WRAPPER_GROUP=" $( mktemp) "
71+ echo " postgres:x:$( id -u) :$( id -g) :PostgreSQL:$PGDATA :/bin/false" > " $NSS_WRAPPER_PASSWD "
72+ echo " postgres:x:$( id -g) :" > " $NSS_WRAPPER_GROUP "
73+ fi
6774
68- file_env ' POSTGRES_USER' ' postgres'
69- file_env ' POSTGRES_PASSWORD'
75+ if [ " $POSTGRES_INITDB_WALDIR " ]; then
76+ set -- --waldir " $POSTGRES_INITDB_WALDIR " " $@ "
77+ fi
7078
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 "
79+ eval ' initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") ' " $POSTGRES_INITDB_ARGS " ' "$@"'
7680
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
81+ # unset/cleanup "nss_wrapper" bits
82+ if [ " ${LD_PRELOAD:- } " = ' /usr/lib/libnss_wrapper.so' ]; then
83+ rm -f " $NSS_WRAPPER_PASSWD " " $NSS_WRAPPER_GROUP "
84+ unset LD_PRELOAD NSS_WRAPPER_PASSWD NSS_WRAPPER_GROUP
85+ fi
86+ }
8287
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
88+ # print large warning if POSTGRES_PASSWORD is empty
89+ docker_verify_minimum_env () {
90+ # check password first so we can output the warning before postgres
91+ # messes it up
92+ if [ " ${# POSTGRES_PASSWORD} " -ge 100 ]; then
93+ cat >&2 << -'EOWARN '
8794
88- if [ " ${# POSTGRES_PASSWORD} " -ge 100 ]; then
89- cat >&2 << -'EOWARN '
95+ WARNING: The supplied POSTGRES_PASSWORD is 100+ characters.
9096
91- WARNING: The supplied POSTGRES_PASSWORD is 100+ characters .
97+ This will not work if used via PGPASSWORD with "psql" .
9298
93- This will not work if used via PGPASSWORD with "psql".
99+ https://www.postgresql.org/message-id/flat/E1Rqxp2-0004Qt-PL%40wrigleys.postgresql.org (BUG #6412)
100+ https://github.com/docker-library/postgres/issues/507
94101
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
102+ EOWARN
103+ fi
104+ if [ -z " $POSTGRES_PASSWORD " ]; then
105+ # The - option suppresses leading tabs but *not* spaces. :)
106+ cat >&2 << -'EOWARN '
107+ ****************************************************
108+ WARNING: No password has been set for the database.
109+ This will allow anyone with access to the
110+ Postgres port to access your database. In
111+ Docker's default configuration, this is
112+ effectively any other container on the same
113+ system.
97114
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
115+ Use "-e POSTGRES_PASSWORD=password" to set
116+ it in "docker run".
117+ ****************************************************
118+ EOWARN
118119
119- {
120- echo
121- echo " host all all all $authMethod "
122- } >> " $PGDATA /pg_hba.conf"
120+ fi
121+ }
123122
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
123+ # usage: docker_process_init_files [file [file [...]]]
124+ # ie: docker_process_init_files /always-initdb.d/*
125+ # process initializer files, based on file extensions and permissions
126+ docker_process_init_files () {
127+ # psql here for backwards compatiblilty "${psql[@]}"
128+ psql=( docker_process_sql )
130129
131- file_env ' POSTGRES_DB' " $POSTGRES_USER "
130+ echo
131+ local f
132+ for f; do
133+ case " $f " in
134+ * .sh)
135+ # https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
136+ # https://github.com/docker-library/postgres/pull/452
137+ if [ -x " $f " ]; then
138+ echo " $0 : running $f "
139+ " $f "
140+ else
141+ echo " $0 : sourcing $f "
142+ . " $f "
143+ fi
144+ ;;
145+ * .sql) echo " $0 : running $f " ; docker_process_sql -f " $f " ; echo ;;
146+ * .sql.gz) echo " $0 : running $f " ; gunzip -c " $f " | docker_process_sql; echo ;;
147+ * ) echo " $0 : ignoring $f " ;;
148+ esac
149+ echo
150+ done
151+ }
132152
133- export PGPASSWORD=" ${PGPASSWORD:- $POSTGRES_PASSWORD } "
134- psql=( psql -v ON_ERROR_STOP=1 --username " $POSTGRES_USER " --no-password )
153+ # Execute sql script, passed via stdin (or -f flag of pqsl)
154+ # usage: docker_process_sql [psql-cli-args]
155+ # ie: docker_process_sql --dbname=mydb <<<'INSERT ...'
156+ # ie: docker_process_sql -f my-file.sql
157+ # ie: docker_process_sql <my-file.sql
158+ docker_process_sql () {
159+ local query_runner=( psql -v ON_ERROR_STOP=1 --username " $POSTGRES_USER " --no-password )
160+ if [ -n " $POSTGRES_DB " ]; then
161+ query_runner+=( --dbname " $POSTGRES_DB " )
162+ fi
135163
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 " )
164+ " ${query_runner[@]} " " $@ "
165+ }
143166
167+ # create initial database
168+ # uses environment variables for input: POSTGRES_DB
169+ docker_setup_db () {
170+ if [ " $POSTGRES_DB " != ' postgres' ]; then
171+ POSTGRES_DB= docker_process_sql --dbname postgres --set db=" $POSTGRES_DB " << -'EOSQL '
172+ CREATE DATABASE :"db" ;
173+ EOSQL
144174 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
175+ fi
176+ }
164177
165- PGUSER=" ${PGUSER:- $POSTGRES_USER } " \
166- pg_ctl -D " $PGDATA " -m fast -w stop
178+ # Loads various settings that are used elsewhere in the script
179+ # This should be called before any other functions
180+ docker_setup_env () {
181+ file_env ' POSTGRES_PASSWORD'
167182
168- unset PGPASSWORD
183+ file_env ' POSTGRES_USER' ' postgres'
184+ file_env ' POSTGRES_DB' " $POSTGRES_USER "
185+ file_env ' POSTGRES_INITDB_ARGS'
169186
187+ declare -g DATABASE_ALREADY_EXISTS
188+ # look specifically for PG_VERSION, as it is expected in the DB dir
189+ if [ -s " $PGDATA /PG_VERSION" ]; then
190+ DATABASE_ALREADY_EXISTS=' true'
191+ fi
192+ }
193+
194+ # append md5 or trust auth to pg_hba.conf based on existence of POSTGRES_PASSWORD
195+ pg_setup_hba_conf () {
196+ local authMethod
197+ if [ " $POSTGRES_PASSWORD " ]; then
198+ authMethod=' md5'
199+ else
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+
236+ if [ " $1 " = ' postgres' ]; then
237+ docker_setup_env
238+ # setup data directories and permissions (when run as root)
239+ docker_create_db_directories
240+ if [ " $( id -u) " = ' 0' ]; then
241+ # then restart script as postgres user
242+ exec su-exec postgres " $BASH_SOURCE " " $@ "
243+ fi
244+
245+ # only run initialization on an empty data directory
246+ if [ -z " $DATABASE_ALREADY_EXISTS " ]; then
247+ docker_verify_minimum_env
248+ docker_init_database_dir
249+ pg_setup_hba_conf
250+
251+ # PGPASSWORD is required for psql when authentication is required for 'local' connections via pg_hba.conf and is otherwise harmless
252+ # e.g. when '--auth=md5' or '--auth-local=md5' is used in POSTGRES_INITDB_ARGS
253+ export PGPASSWORD=" ${PGPASSWORD:- $POSTGRES_PASSWORD } "
254+ docker_temp_server_start " $@ "
255+
256+ docker_setup_db
257+ docker_process_init_files /docker-entrypoint-initdb.d/*
258+
259+ docker_temp_server_stop
260+ unset PGPASSWORD
261+
262+ echo
263+ echo ' PostgreSQL init process complete; ready for start up.'
264+ echo
265+ else
266+ echo
267+ echo ' PostgreSQL Database directory appears to contain a database; Skipping initialization'
268+ echo
269+ fi
270+ fi
271+
272+ exec " $@ "
273+ }
175274
176- exec " $@ "
275+ if ! _is_sourced; then
276+ _main " $@ "
277+ fi
0 commit comments