@@ -24,153 +24,254 @@ file_env() {
24
24
unset " $fileVar "
25
25
}
26
26
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) "
30
38
31
- # allow the container to be started with `--user`
32
- if [ " $1 " = ' postgres' ] && [ " $( id -u) " = ' 0' ]; then
33
39
mkdir -p " $PGDATA "
34
- chown -R postgres " $PGDATA "
35
40
chmod 700 " $PGDATA "
36
41
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 || :
40
45
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
42
47
if [ " $POSTGRES_INITDB_WALDIR " ]; then
43
48
mkdir -p " $POSTGRES_INITDB_WALDIR "
44
- chown -R postgres " $POSTGRES_INITDB_WALDIR "
49
+ [ " $user " = ' 0 ' ] && find " $POSTGRES_INITDB_WALDIR " \! -user postgres - exec chown postgres ' {} ' +
45
50
chmod 700 " $POSTGRES_INITDB_WALDIR "
46
51
fi
47
52
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
+ }
55
59
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
67
74
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
70
78
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 " ' "$@"'
76
80
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
+ }
82
87
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 '
87
94
88
- if [ " ${# POSTGRES_PASSWORD} " -ge 100 ]; then
89
- cat >&2 << -'EOWARN '
95
+ WARNING: The supplied POSTGRES_PASSWORD is 100+ characters.
90
96
91
- WARNING: The supplied POSTGRES_PASSWORD is 100+ characters .
97
+ This will not work if used via PGPASSWORD with "psql" .
92
98
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
94
101
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.
97
114
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
118
119
119
- {
120
- echo
121
- echo " host all all all $authMethod "
122
- } >> " $PGDATA /pg_hba.conf"
120
+ fi
121
+ }
123
122
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 )
130
129
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
+ }
132
152
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
135
163
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
+ }
143
166
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
144
174
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
+ }
164
177
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'
167
182
168
- unset PGPASSWORD
183
+ file_env ' POSTGRES_USER' ' postgres'
184
+ file_env ' POSTGRES_DB' " $POSTGRES_USER "
185
+ file_env ' POSTGRES_INITDB_ARGS'
169
186
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
+ {
170
204
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
173
214
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
+ }
175
274
176
- exec " $@ "
275
+ if ! _is_sourced; then
276
+ _main " $@ "
277
+ fi
0 commit comments