@@ -28,7 +28,8 @@ const (
2828 configDatabaseURIPath = "~postgres-operator/config-database-uri"
2929 ldapFilePath = "~postgres-operator/ldap-bind-password"
3030 gunicornConfigFilePath = "~postgres-operator/" + gunicornConfigKey
31- oauthConfigDir = "~postgres-operator/oauth-config/"
31+ oauthConfigDir = "~postgres-operator/oauth-config"
32+ oauthAbsolutePath = configMountPath + "/" + oauthConfigDir
3233
3334 // scriptMountPath is where to mount a temporary directory that is only
3435 // writable during Pod initialization.
@@ -49,15 +50,14 @@ func pod(
4950 inConfigMap * corev1.ConfigMap ,
5051 outPod * corev1.PodSpec ,
5152 pgAdminVolume * corev1.PersistentVolumeClaim ,
52- oauthSecrets []corev1.Secret ,
5353) {
5454 // create the projected volume of config maps for use in
5555 // 1. dynamic server discovery
5656 // 2. adding the config variables during pgAdmin startup
5757 configVolume := corev1.Volume {Name : "pgadmin-config" }
5858 configVolume .VolumeSource = corev1.VolumeSource {
5959 Projected : & corev1.ProjectedVolumeSource {
60- Sources : podConfigFiles (inConfigMap , * inPGAdmin , oauthSecrets ),
60+ Sources : podConfigFiles (inConfigMap , * inPGAdmin ),
6161 },
6262 }
6363
@@ -187,8 +187,7 @@ func pod(
187187
188188// podConfigFiles returns projections of pgAdmin's configuration files to
189189// 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 {
192191
193192 config := append (append ([]corev1.VolumeProjection {}, pgadmin .Spec .Config .Files ... ),
194193 []corev1.VolumeProjection {
@@ -215,22 +214,15 @@ func podConfigFiles(configmap *corev1.ConfigMap, pgadmin v1beta1.PGAdmin,
215214 },
216215 }... )
217216
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+ })
234226 }
235227
236228 if pgadmin .Spec .Config .ConfigDatabaseURI != nil {
@@ -332,15 +324,17 @@ loadServerCommand
332324 // descriptor and uses the timeout of the builtin `read` to wait. That same
333325 // descriptor gets closed and reopened to use the builtin `[ -nt` to check mtimes.
334326 // - 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.
337330 // - https://github.com/benoitc/gunicorn/issues/3353
331+ //
338332 // Right now the config file is on the same configMap as the cluster file
339333 // so if the mtime changes for any of those files, it will change for all.
340334 var reloadScript = `
341335exec {fd}<> <(:||:)
342336while 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?});
344338 then
345339 exec {fd}>&- && exec {fd}<> <(:||:)
346340 stat --format='Loaded shared servers dated %y' "${cluster_file}"
@@ -394,28 +388,33 @@ import glob, json, re, os
394388DEFAULT_BINARY_PATHS = {'pg': sorted([''] + glob.glob('/usr/pgsql-*/bin')).pop()}
395389with open('` + configMountPath + `/` + configFilePath + `') as _f:
396390 _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}")
411391 if type(_data) is dict:
412392 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
413411if os.path.isfile('` + ldapPasswordAbsolutePath + `'):
414412 with open('` + ldapPasswordAbsolutePath + `') as _f:
415413 LDAP_BIND_PASSWORD = _f.read()
416414if os.path.isfile('` + configDatabaseURIPathAbsolutePath + `'):
417415 with open('` + configDatabaseURIPathAbsolutePath + `') as _f:
418416 CONFIG_DATABASE_URI = _f.read()
417+ del _conf, _data, _f
419418`
420419
421420 // Gunicorn reads from the `/etc/pgadmin/gunicorn_config.py` file during startup
0 commit comments