diff twisted/plugins/pubsub.py @ 428:34bd55179e22

environment variables can now be used to set options: if an environment variable is set using `SAT_PUBSUB_[OPTION NAME]` it will be used if not specified directly in command line. Options are retrieved in this order of priority: - command line (highest priority) - environment variable - option set in `sat.conf` For database options, following PostgreSQL aliases can also be used: PGUSER, PGDATABASE, PGPASSWORD, PGHOST, PGPORT.
author Goffi <goffi@goffi.org>
date Fri, 09 Oct 2020 18:26:50 +0200
parents 6f8e1c180c83
children 5a0ada3b61ca
line wrap: on
line diff
--- a/twisted/plugins/pubsub.py	Fri Oct 09 18:22:45 2020 +0200
+++ b/twisted/plugins/pubsub.py	Fri Oct 09 18:26:50 2020 +0200
@@ -52,6 +52,7 @@
 
 import sys
 import csv
+import os
 from os.path import expanduser, realpath
 import configparser
 # patch for Python 3.8 compatibility
@@ -94,12 +95,31 @@
     ['db_host', None, None, 'Database host (pgsql backend)'],
     ['db_port', None, None, 'Database port (pgsql backend)'],
     ]
-# here for future use
+
 OPT_PARAMETERS_CFG = [
     ["admins_jids_list", None, [], "List of administrators' bare jids",
      coerceJidListType]
     ]
 
+# prefix used for environment variables
+ENV_PREFIX = "SAT_PUBSUB_"
+# mapping from option name to environment variables to use
+# each parameter name links to a list of variable environment name
+# if an environment variable of one of the names exists it will be used
+# as default value, with priority over config file
+ENV_OPT_MAP = {
+    # we use the same environment variables as PostgreSQL
+    'db_user': ['PGUSER'],
+    'db_name': ['PGDATABASE'],
+    'db_pass': ['PGPASSWORD'],
+    'db_host': ['PGHOST'],
+    'db_port': ['PGPORT'],
+}
+for opt in OPT_PARAMETERS_BOTH + OPT_PARAMETERS_CFG:
+    name = opt[0]
+    env_name = f"{ENV_PREFIX}{name.upper()}"
+    ENV_OPT_MAP.setdefault(name, []).append(env_name)
+
 CONFIG_FILENAME = 'sat'
 # List of the configuration filenames sorted by ascending priority
 CONFIG_FILES = [realpath(expanduser(path) + CONFIG_FILENAME + '.conf') for path in (
@@ -135,22 +155,37 @@
         config_parser.read(CONFIG_FILES)
         for param in self.optParameters + OPT_PARAMETERS_CFG:
             name = param[0]
-            try:
-                value = config_parser.get(CONFIG_SECTION, name)
+            for env_name in ENV_OPT_MAP[name]:
+                # we first check if value is set as an environment variable
+                value = os.getenv(env_name)
+                if value is not None:
+                    self.setDefaultOption(param, value)
+                    break
+            else:
+                # no environment variable set, let's try with configuration
                 try:
-                    param[2] = param[4](value)
-                except IndexError: # the coerce method is optional
-                    param[2] = value
-                except Exception as e:
-                    log.err('Invalid value for setting "{name}": {msg}'.format(
-                        name=name, msg=e))
-                    sys.exit(1)
-            except (configparser.NoSectionError, configparser.NoOptionError):
-                pass
+                    value = config_parser.get(CONFIG_SECTION, name)
+                    self.setDefaultOption(param, value)
+                except (configparser.NoSectionError, configparser.NoOptionError):
+                    pass
         usage.Options.__init__(self)
         for opt_data in OPT_PARAMETERS_CFG:
             self[opt_data[0]] = opt_data[2]
 
+    def setDefaultOption(self, param, value):
+        """Set default option value using coerce method when needed
+
+        If the value is invalid, we quit the program with exit code 1
+        """
+        try:
+            param[2] = param[4](value)
+        except IndexError: # the coerce method is optional
+            param[2] = value
+        except Exception as e:
+            log.err('Invalid value for setting "{name}": {msg}'.format(
+                name=name, msg=e))
+            sys.exit(1)
+
     def postOptions(self):
         if self['backend'] not in ['pgsql', 'memory']:
             raise usage.UsageError("Unknown backend!")