@@ -28,7 +28,8 @@ const (
28
28
configDatabaseURIPath = "~postgres-operator/config-database-uri"
29
29
ldapFilePath = "~postgres-operator/ldap-bind-password"
30
30
gunicornConfigFilePath = "~postgres-operator/" + gunicornConfigKey
31
- oauthConfigDir = "~postgres-operator/oauth-config/"
31
+ oauthConfigDir = "~postgres-operator/oauth-config"
32
+ oauthAbsolutePath = configMountPath + "/" + oauthConfigDir
32
33
33
34
// scriptMountPath is where to mount a temporary directory that is only
34
35
// writable during Pod initialization.
@@ -49,15 +50,14 @@ func pod(
49
50
inConfigMap * corev1.ConfigMap ,
50
51
outPod * corev1.PodSpec ,
51
52
pgAdminVolume * corev1.PersistentVolumeClaim ,
52
- oauthSecrets []corev1.Secret ,
53
53
) {
54
54
// create the projected volume of config maps for use in
55
55
// 1. dynamic server discovery
56
56
// 2. adding the config variables during pgAdmin startup
57
57
configVolume := corev1.Volume {Name : "pgadmin-config" }
58
58
configVolume .VolumeSource = corev1.VolumeSource {
59
59
Projected : & corev1.ProjectedVolumeSource {
60
- Sources : podConfigFiles (inConfigMap , * inPGAdmin , oauthSecrets ),
60
+ Sources : podConfigFiles (inConfigMap , * inPGAdmin ),
61
61
},
62
62
}
63
63
@@ -187,8 +187,7 @@ func pod(
187
187
188
188
// podConfigFiles returns projections of pgAdmin's configuration files to
189
189
// include in the configuration volume.
190
- func podConfigFiles (configmap * corev1.ConfigMap , pgadmin v1beta1.PGAdmin ,
191
- oauthSecrets []corev1.Secret ) []corev1.VolumeProjection {
190
+ func podConfigFiles (configmap * corev1.ConfigMap , pgadmin v1beta1.PGAdmin ) []corev1.VolumeProjection {
192
191
193
192
config := append (append ([]corev1.VolumeProjection {}, pgadmin .Spec .Config .Files ... ),
194
193
[]corev1.VolumeProjection {
@@ -215,22 +214,15 @@ func podConfigFiles(configmap *corev1.ConfigMap, pgadmin v1beta1.PGAdmin,
215
214
},
216
215
}... )
217
216
218
- if pgadmin .Spec .Config .OauthConfigurations != nil {
219
- for _ , secret := range oauthSecrets {
220
- config = append (config , corev1.VolumeProjection {
221
- Secret : & corev1.SecretProjection {
222
- LocalObjectReference : corev1.LocalObjectReference {
223
- Name : secret .Name ,
224
- },
225
- Items : []corev1.KeyToPath {
226
- {
227
- Key : "oauth-config" ,
228
- Path : fmt .Sprintf ("%s%s.json" , oauthConfigDir , secret .Name ),
229
- },
230
- },
231
- },
232
- })
233
- }
217
+ for i , oauth := range pgadmin .Spec .Config .OAuthConfigurations {
218
+ // Safely encode the OAUTH2_NAME in the file name. Prepend the index so
219
+ // the files can be loaded in the order they are defined in the spec.
220
+ mountPath := fmt .Sprintf (
221
+ "%s/%02d-%s.json" , oauthConfigDir , i , shell .CleanFileName (oauth .Name ),
222
+ )
223
+ config = append (config , corev1.VolumeProjection {
224
+ Secret : initialize .Pointer (oauth .Secret .AsProjection (mountPath )),
225
+ })
234
226
}
235
227
236
228
if pgadmin .Spec .Config .ConfigDatabaseURI != nil {
@@ -332,15 +324,17 @@ loadServerCommand
332
324
// descriptor and uses the timeout of the builtin `read` to wait. That same
333
325
// descriptor gets closed and reopened to use the builtin `[ -nt` to check mtimes.
334
326
// - https://unix.stackexchange.com/a/407383
335
- // In order to get gunicorn to reload the logging config
336
- // we need to send a KILL rather than a HUP signal.
327
+ //
328
+ // Gunicorn needs a SIGTERM rather than SIGHUP to reload its logging config.
329
+ // This also causes pgAdmin to restart when its configuration changes.
337
330
// - https://github.com/benoitc/gunicorn/issues/3353
331
+ //
338
332
// Right now the config file is on the same configMap as the cluster file
339
333
// so if the mtime changes for any of those files, it will change for all.
340
334
var reloadScript = `
341
335
exec {fd}<> <(:||:)
342
336
while read -r -t 5 -u "${fd}" ||:; do
343
- if [[ "${cluster_file}" -nt "/proc/self/fd/${fd}" ]] && loadServerCommand && kill -KILL $(head -1 ${PGADMIN4_PIDFILE?});
337
+ if [[ "${cluster_file}" -nt "/proc/self/fd/${fd}" ]] && loadServerCommand && kill -TERM $(head -1 ${PGADMIN4_PIDFILE?});
344
338
then
345
339
exec {fd}>&- && exec {fd}<> <(:||:)
346
340
stat --format='Loaded shared servers dated %y' "${cluster_file}"
@@ -394,28 +388,33 @@ import glob, json, re, os
394
388
DEFAULT_BINARY_PATHS = {'pg': sorted([''] + glob.glob('/usr/pgsql-*/bin')).pop()}
395
389
with open('` + configMountPath + `/` + configFilePath + `') as _f:
396
390
_conf, _data = re.compile(r'[A-Z_0-9]+'), json.load(_f)
397
- folder_path = '` + configMountPath + `/` + oauthConfigDir + `'
398
- if os.path.isdir(folder_path):
399
- for filename in os.listdir(folder_path):
400
- with open(os.path.join(folder_path, filename), "r", encoding="utf-8") as f:
401
- try:
402
- oath = json.load(f)
403
- if oath.get("OAUTH2_NAME") not in [
404
- o.get("OAUTH2_NAME") for o in _data.get("OAUTH2_CONFIG")]:
405
- _data.get("OAUTH2_CONFIG").append(oath)
406
- for o in _data.get("OAUTH2_CONFIG"):
407
- if o.get("OAUTH2_NAME") == oath.get("OAUTH2_NAME"):
408
- o.update(oath)
409
- except Exception as e:
410
- print(f"An unexpected error occurred: {e}")
411
391
if type(_data) is dict:
412
392
globals().update({k: v for k, v in _data.items() if _conf.fullmatch(k)})
393
+ if 'OAUTH2_CONFIG' in globals() and type(OAUTH2_CONFIG) is list:
394
+ OAUTH2_CONFIG = [_conf for _conf in OAUTH2_CONFIG if type(_conf) is dict and 'OAUTH2_NAME' in _conf]
395
+ for _f in reversed(glob.glob('` + oauthAbsolutePath + `/[0-9][0-9]-*.json')):
396
+ if 'OAUTH2_CONFIG' not in globals() or type(OAUTH2_CONFIG) is not list:
397
+ OAUTH2_CONFIG = []
398
+ try:
399
+ with open(_f) as _f:
400
+ _data, _name = json.load(_f), os.path.basename(_f.name)[3:-5]
401
+ _data, _next = { 'OAUTH2_NAME': _name } | _data, []
402
+ for _conf in OAUTH2_CONFIG:
403
+ if _data['OAUTH2_NAME'] == _conf.get('OAUTH2_NAME'):
404
+ _data = _conf | _data
405
+ else:
406
+ _next.append(_conf)
407
+ OAUTH2_CONFIG = [_data] + _next
408
+ del _next
409
+ except:
410
+ pass
413
411
if os.path.isfile('` + ldapPasswordAbsolutePath + `'):
414
412
with open('` + ldapPasswordAbsolutePath + `') as _f:
415
413
LDAP_BIND_PASSWORD = _f.read()
416
414
if os.path.isfile('` + configDatabaseURIPathAbsolutePath + `'):
417
415
with open('` + configDatabaseURIPathAbsolutePath + `') as _f:
418
416
CONFIG_DATABASE_URI = _f.read()
417
+ del _conf, _data, _f
419
418
`
420
419
421
420
// Gunicorn reads from the `/etc/pgadmin/gunicorn_config.py` file during startup
0 commit comments