Skip to content

Commit 0c29c35

Browse files
authored
Merge pull request docker-library#496 from infosiftr/functionalization
Functionalize the entrypoint to allow outside sourcing for extreme customizing of startup
2 parents a8613f4 + 8fada98 commit 0c29c35

13 files changed

+2821
-1521
lines changed

10/alpine/docker-entrypoint.sh

Lines changed: 217 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy