@@ -24,153 +24,237 @@ 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[${#FUNCNAME[@]} - 1]}" == ' source' ]
31+ }
32+
33+ # used to create initial posgres directories and if run as root, ensure ownership to the "postgres" user
34+ create_postgres_dirs () {
35+ local user=" $( id -u) "
3036
31- # allow the container to be started with `--user`
32- if [ " $1 " = ' postgres' ] && [ " $( id -u) " = ' 0' ]; then
3337 mkdir -p " $PGDATA "
34- chown -R postgres " $PGDATA "
3538 chmod 700 " $PGDATA "
3639
37- mkdir -p /var/run/postgresql
38- chown -R postgres /var/run/postgresql
39- chmod 775 /var/run/postgresql
40+ # ignore failure since it will be fine when using the image provided directory; see also https://github.com/docker-library/postgres/pull/289
41+ mkdir -p /var/run/postgresql || :
42+ chmod 775 /var/run/postgresql || :
4043
41- # Create the transaction log directory before initdb is run (below) so the directory is owned by the correct user
44+ # Create the transaction log directory before initdb is run so the directory is owned by the correct user
4245 if [ " $POSTGRES_INITDB_WALDIR " ]; then
4346 mkdir -p " $POSTGRES_INITDB_WALDIR "
44- chown -R postgres " $POSTGRES_INITDB_WALDIR "
47+ [ " $user " = ' 0 ' ] && find " $POSTGRES_INITDB_WALDIR " \! -user postgres - exec chown postgres ' {} ' +
4548 chmod 700 " $POSTGRES_INITDB_WALDIR "
4649 fi
4750
48- exec gosu postgres " $BASH_SOURCE " " $@ "
49- fi
51+ # allow the container to be started with `--user`
52+ if [ " $user " = ' 0' ]; then
53+ find " $PGDATA " \! -user postgres -exec chown postgres ' {}' +
54+ find /var/run/postgresql \! -user postgres -exec chown postgres ' {}' +
55+ fi
56+ }
5057
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-
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
58+ # initialize empty PGDATA directory with new database via 'initdb'
59+ init_pgdata () {
60+ # "initdb" is particular about the current user existing in "/etc/passwd", so we use "nss_wrapper" to fake that if necessary
61+ # see https://github.com/docker-library/postgres/pull/253, https://github.com/docker-library/postgres/issues/359, https://cwrap.org/nss_wrapper.html
62+ if ! getent passwd " $( id -u) " & > /dev/null && [ -e /usr/lib/libnss_wrapper.so ]; then
63+ export LD_PRELOAD=' /usr/lib/libnss_wrapper.so'
64+ export NSS_WRAPPER_PASSWD=" $( mktemp) "
65+ export NSS_WRAPPER_GROUP=" $( mktemp) "
66+ echo " postgres:x:$( id -u) :$( id -g) :PostgreSQL:$PGDATA :/bin/false" > " $NSS_WRAPPER_PASSWD "
67+ echo " postgres:x:$( id -g) :" > " $NSS_WRAPPER_GROUP "
68+ fi
6769
68- file_env ' POSTGRES_USER' ' postgres'
69- file_env ' POSTGRES_PASSWORD'
70+ file_env ' POSTGRES_INITDB_ARGS'
71+ if [ " $POSTGRES_INITDB_WALDIR " ]; then
72+ export POSTGRES_INITDB_ARGS=" $POSTGRES_INITDB_ARGS --waldir $POSTGRES_INITDB_WALDIR "
73+ fi
7074
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 "
75+ eval ' initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") ' " $POSTGRES_INITDB_ARGS "
7676
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
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
82+ }
8283
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
84+ # print large warning if POSTGRES_PASSWORD is empty
85+ print_password_warning () {
86+ # check password first so we can output the warning before postgres
87+ # messes it up
88+ if [ " ${# POSTGRES_PASSWORD} " -ge 100 ]; then
89+ cat >&2 << -'EOWARN '
8790
88- if [ " ${# POSTGRES_PASSWORD} " -ge 100 ]; then
89- cat >&2 << -'EOWARN '
91+ WARNING: The supplied POSTGRES_PASSWORD is 100+ characters.
9092
91- WARNING: The supplied POSTGRES_PASSWORD is 100+ characters .
93+ This will not work if used via PGPASSWORD with "psql" .
9294
93- This will not work if used via PGPASSWORD with "psql".
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
9497
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
98+ EOWARN
99+ fi
100+ if [ -z " $POSTGRES_PASSWORD " ]; then
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.
97110
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
111+ Use "-e POSTGRES_PASSWORD=password" to set
112+ it in "docker run".
113+ ****************************************************
114+ EOWARN
118115
119- {
120- echo
121- echo " host all all all $authMethod "
122- } >> " $PGDATA /pg_hba.conf"
116+ fi
117+ }
123118
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
119+ # run, source, or read files from /docker-entrypoint-initdb.d (or specified directory)
120+ process_init_files () {
121+ # psql here for backwards compatiblilty "${psql[@]}"
122+ psql=( psql_run )
130123
131- file_env ' POSTGRES_DB ' " $POSTGRES_USER "
124+ local initDir= " ${1 :-/ docker-entrypoint-initdb.d} "
132125
133- export PGPASSWORD=" ${PGPASSWORD:- $POSTGRES_PASSWORD } "
134- psql=( psql -v ON_ERROR_STOP=1 --username " $POSTGRES_USER " --no-password )
126+ echo
127+ for f in " ${initDir%/ } " /* ; do
128+ case " $f " in
129+ * .sh)
130+ # https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
131+ # https://github.com/docker-library/postgres/pull/452
132+ if [ -x " $f " ]; then
133+ echo " $0 : running $f "
134+ " $f "
135+ else
136+ echo " $0 : sourcing $f "
137+ . " $f "
138+ fi
139+ ;;
140+ * .sql) echo " $0 : running $f " ; psql_run -f " $f " ; echo ;;
141+ * .sql.gz) echo " $0 : running $f " ; gunzip -c " $f " | psql_run; echo ;;
142+ * ) echo " $0 : ignoring $f " ;;
143+ esac
144+ echo
145+ done
146+ }
135147
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 " )
148+ # run `psql` with proper arguments for user and db
149+ psql_run () {
150+ local query_runner=( psql -v ON_ERROR_STOP=1 --username " $POSTGRES_USER " --no-password )
151+ if [ -n " $POSTGRES_DB " ]; then
152+ query_runner+=( --dbname " $POSTGRES_DB " )
153+ fi
154+
155+ " ${query_runner[@]} " " $@ "
156+ }
143157
158+ # create initial postgresql superuser with password and database
159+ # uses environment variables for input: POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB
160+ setup_database () {
161+ if [ " $POSTGRES_DB " != ' postgres' ]; then
162+ POSTGRES_DB= psql_run --dbname postgres --set db=" $POSTGRES_DB " << -'EOSQL '
163+ CREATE DATABASE :"db" ;
164+ EOSQL
144165 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
166+ fi
167+ }
164168
165- PGUSER=" ${PGUSER:- $POSTGRES_USER } " \
166- pg_ctl -D " $PGDATA " -m fast -w stop
169+ # get user/pass and db from env vars or via file
170+ setup_env_vars () {
171+ file_env ' POSTGRES_PASSWORD'
167172
168- unset PGPASSWORD
173+ file_env ' POSTGRES_USER' ' postgres'
174+ file_env ' POSTGRES_DB' " $POSTGRES_USER "
175+ }
169176
177+ # append md5 or trust auth to pg_hba.conf based on existence of POSTGRES_PASSWORD
178+ setup_pg_hba () {
179+ local authMethod
180+ if [ " $POSTGRES_PASSWORD " ]; then
181+ authMethod=' md5'
182+ else
183+ authMethod=' trust'
184+ fi
185+
186+ {
170187 echo
171- echo ' PostgreSQL init process complete; ready for start up.'
172- echo
188+ echo " host all all all $authMethod "
189+ } >> " $PGDATA /pg_hba.conf"
190+ }
191+
192+ # start socket-only postgresql server for setting up user or running scripts
193+ temporary_pgserver_start () {
194+ # internal start of server in order to allow set-up using psql-client
195+ # does not listen on external TCP/IP and waits until start finishes
196+ PGUSER=" ${PGUSER:- $POSTGRES_USER } " \
197+ pg_ctl -D " $PGDATA " \
198+ -o " -c listen_addresses=''" \
199+ -w start
200+ # ??? "$@"
201+ }
202+
203+ # stop postgresql server after done setting up user and running scripts
204+ temporary_pgserver_stop () {
205+ PGUSER=" ${PGUSER:- postgres} " \
206+ pg_ctl -D " $PGDATA " -m fast -w stop
207+ }
208+
209+ main () {
210+ # if first arg looks like a flag, assume we want to run postgres server
211+ if [ " ${1: 0: 1} " = ' -' ]; then
212+ set -- postgres " $@ "
173213 fi
174- fi
175214
176- exec " $@ "
215+ # setup data directories and permissions, then restart script as postgres user
216+ if [ " $1 " = ' postgres' ] && [ " $( id -u) " = ' 0' ]; then
217+ create_postgres_dirs
218+ exec gosu postgres " $BASH_SOURCE " " $@ "
219+ fi
220+
221+ if [ " $1 " = ' postgres' ]; then
222+ create_postgres_dirs
223+
224+ # only run initialization on an empty data directory
225+ # look specifically for PG_VERSION, as it is expected in the DB dir
226+ if [ ! -s " $PGDATA /PG_VERSION" ]; then
227+ init_pgdata
228+
229+ setup_env_vars
230+ print_password_warning
231+ setup_pg_hba
232+
233+ # PGPASSWORD is required for psql when authentication is required for 'local' connections via pg_hba.conf and is otherwise harmless
234+ # e.g. when '--auth=md5' or '--auth-local=md5' is used in POSTGRES_INITDB_ARGS
235+ export PGPASSWORD=" ${PGPASSWORD:- $POSTGRES_PASSWORD } "
236+ temporary_pgserver_start
237+
238+ setup_database
239+
240+ process_init_files
241+
242+ temporary_pgserver_stop
243+ unset PGPASSWORD
244+
245+ echo
246+ echo ' PostgreSQL init process complete; ready for start up.'
247+ echo
248+ else
249+ echo
250+ echo ' PostgreSQL Database directory appears to contain a database; Skipping initialization'
251+ echo
252+ fi
253+ fi
254+
255+ exec " $@ "
256+ }
257+
258+ if ! _is_sourced; then
259+ main " $@ "
260+ fi
0 commit comments