@@ -28,10 +28,11 @@ import (
28
28
)
29
29
30
30
const (
31
- configMountPath = "/etc/pgadmin/conf.d"
32
- configFilePath = "~postgres-operator/" + settingsConfigMapKey
33
- clusterFilePath = "~postgres-operator/" + settingsClusterMapKey
34
- ldapFilePath = "~postgres-operator/ldap-bind-password"
31
+ configMountPath = "/etc/pgadmin/conf.d"
32
+ configFilePath = "~postgres-operator/" + settingsConfigMapKey
33
+ clusterFilePath = "~postgres-operator/" + settingsClusterMapKey
34
+ ldapFilePath = "~postgres-operator/ldap-bind-password"
35
+ gunicornConfigFilePath = "~postgres-operator/" + gunicornConfigKey
35
36
36
37
// Nothing should be mounted to this location except the script our initContainer writes
37
38
scriptMountPath = "/etc/pgadmin"
@@ -210,6 +211,10 @@ func podConfigFiles(configmap *corev1.ConfigMap, pgadmin v1beta1.PGAdmin) []core
210
211
Key : settingsClusterMapKey ,
211
212
Path : clusterFilePath ,
212
213
},
214
+ {
215
+ Key : gunicornConfigKey ,
216
+ Path : gunicornConfigFilePath ,
217
+ },
213
218
},
214
219
},
215
220
},
@@ -262,32 +267,43 @@ func startupScript(pgadmin *v1beta1.PGAdmin) []string {
262
267
var setupCommandV7 = "python3 ${PGADMIN_DIR}/setup.py"
263
268
var setupCommandV8 = setupCommandV7 + " setup-db"
264
269
270
+ // startCommands (v8 image includes Gunicorn)
271
+ var startCommandV7 = "pgadmin4 &"
272
+ var startCommandV8 = "gunicorn -c /etc/pgadmin/gunicorn_config.py --chdir $PGADMIN_DIR pgAdmin4:app &"
273
+
265
274
// This script sets up, starts pgadmin, and runs the appropriate `loadServerCommand` to register the discovered servers.
275
+ // pgAdmin is hosted by Gunicorn and uses a config file.
276
+ // - https://www.pgadmin.org/docs/pgadmin4/development/server_deployment.html#standalone-gunicorn-configuration
277
+ // - https://docs.gunicorn.org/en/latest/configure.html
266
278
var startScript = fmt .Sprintf (`
267
279
PGADMIN_DIR=/usr/local/lib/python3.11/site-packages/pgadmin4
268
280
APP_RELEASE=$(cd $PGADMIN_DIR && python3 -c "import config; print(config.APP_RELEASE)")
269
281
270
282
echo "Running pgAdmin4 Setup"
271
283
if [ $APP_RELEASE -eq 7 ]; then
272
- %s
284
+ %s
273
285
else
274
- %s
286
+ %s
275
287
fi
276
288
277
289
echo "Starting pgAdmin4"
278
290
PGADMIN4_PIDFILE=/tmp/pgadmin4.pid
279
- pgadmin4 &
291
+ if [ $APP_RELEASE -eq 7 ]; then
292
+ %s
293
+ else
294
+ %s
295
+ fi
280
296
echo $! > $PGADMIN4_PIDFILE
281
297
282
298
loadServerCommand() {
283
- if [ $APP_RELEASE -eq 7 ]; then
284
- %s
285
- else
286
- %s
287
- fi
299
+ if [ $APP_RELEASE -eq 7 ]; then
300
+ %s
301
+ else
302
+ %s
303
+ fi
288
304
}
289
305
loadServerCommand
290
- ` , setupCommandV7 , setupCommandV8 , loadServerCommandV7 , loadServerCommandV8 )
306
+ ` , setupCommandV7 , setupCommandV8 , startCommandV7 , startCommandV8 , loadServerCommandV7 , loadServerCommandV8 )
291
307
292
308
// Use a Bash loop to periodically check:
293
309
// 1. the mtime of the mounted configuration volume for shared/discovered servers.
@@ -303,17 +319,21 @@ loadServerCommand
303
319
var reloadScript = `
304
320
exec {fd}<> <(:)
305
321
while read -r -t 5 -u "${fd}" || true; do
306
- if [ "${cluster_file}" -nt "/proc/self/fd/${fd}" ] && loadServerCommand
307
- then
308
- exec {fd}>&- && exec {fd}<> <(:)
309
- stat --format='Loaded shared servers dated %y' "${cluster_file}"
310
- fi
311
- if [ ! -d /proc/$(cat $PGADMIN4_PIDFILE) ]
312
- then
313
- pgadmin4 &
314
- echo $! > $PGADMIN4_PIDFILE
315
- echo "Restarting pgAdmin4"
316
- fi
322
+ if [ "${cluster_file}" -nt "/proc/self/fd/${fd}" ] && loadServerCommand
323
+ then
324
+ exec {fd}>&- && exec {fd}<> <(:)
325
+ stat --format='Loaded shared servers dated %y' "${cluster_file}"
326
+ fi
327
+ if [ ! -d /proc/$(cat $PGADMIN4_PIDFILE) ]
328
+ then
329
+ if [ $APP_RELEASE -eq 7 ]; then
330
+ ` + startCommandV7 + `
331
+ else
332
+ ` + startCommandV8 + `
333
+ fi
334
+ echo $! > $PGADMIN4_PIDFILE
335
+ echo "Restarting pgAdmin4"
336
+ fi
317
337
done
318
338
`
319
339
@@ -333,8 +353,8 @@ func startupCommand() []string {
333
353
// and sets those variables globally. That way those values are available as pgAdmin
334
354
// configurations when pgAdmin starts.
335
355
//
336
- // Note: All pgAdmin settings are uppercase with underscores, so ignore any keys/names
337
- // that are not.
356
+ // Note: All pgAdmin settings are uppercase alphanumeric with underscores, so ignore
357
+ // any keys/names that are not.
338
358
//
339
359
// Note: set pgAdmin's LDAP_BIND_PASSWORD setting from the Secret last
340
360
// in order to overwrite configuration of LDAP_BIND_PASSWORD via ConfigMap JSON.
@@ -352,17 +372,36 @@ with open('` + configMountPath + `/` + configFilePath + `') as _f:
352
372
if os.path.isfile('` + ldapPasswordAbsolutePath + `'):
353
373
with open('` + ldapPasswordAbsolutePath + `') as _f:
354
374
LDAP_BIND_PASSWORD = _f.read()
375
+ `
376
+ // gunicorn reads from the `/etc/pgadmin/gunicorn_config.py` file during startup
377
+ // after all other config files.
378
+ // - https://docs.gunicorn.org/en/latest/configure.html#configuration-file
379
+ //
380
+ // This command writes a script in `/etc/pgadmin/gunicorn_config.py` that reads
381
+ // from the `gunicorn-config.json` file and sets those variables globally.
382
+ // That way those values are available as settings when gunicorn starts.
383
+ //
384
+ // Note: All gunicorn settings are lowercase with underscores, so ignore
385
+ // any keys/names that are not.
386
+ gunicornConfig = `
387
+ import json, re
388
+ with open('` + configMountPath + `/` + gunicornConfigFilePath + `') as _f:
389
+ _conf, _data = re.compile(r'[a-z_]+'), json.load(_f)
390
+ if type(_data) is dict:
391
+ globals().update({k: v for k, v in _data.items() if _conf.fullmatch(k)})
355
392
`
356
393
)
357
394
358
- args := []string {strings .TrimLeft (configSystem , "\n " )}
395
+ args := []string {strings .TrimLeft (configSystem , "\n " ), strings . TrimLeft ( gunicornConfig , " \n " ) }
359
396
360
397
script := strings .Join ([]string {
361
398
// Use the initContainer to create this path to avoid the error noted here:
362
399
// - https://github.com/kubernetes/kubernetes/issues/121294
363
400
`mkdir -p /etc/pgadmin/conf.d` ,
364
401
// Write the system configuration into a read-only file.
365
402
`(umask a-w && echo "$1" > ` + scriptMountPath + `/config_system.py` + `)` ,
403
+ // Write the server configuration into a read-only file.
404
+ `(umask a-w && echo "$2" > ` + scriptMountPath + `/gunicorn_config.py` + `)` ,
366
405
}, "\n " )
367
406
368
407
return append ([]string {"bash" , "-ceu" , "--" , script , "startup" }, args ... )
0 commit comments