Repository: benoitc/gunicorn

Issue:
Environmental variables not available during '--preload' application start up
Environmental variables set through Gunicorn via the '-e' CLI flag or the 'raw_env' configuration setting are not available during application start up when the application uses the '--preload' flag. 

An example program to demonstrate this (using Flask 0.10.1 and Gunicorn 18.0) is here:

https://gist.github.com/tshlabs/eb59186648fd003bf28c

The output from this is the following:

```
$ gunicorn -b localhost:8000 --preload -e FOO=bar test:application
This is the bootstrap. FOO = None
2014-05-05 20:56:14 [2575] [INFO] Starting gunicorn 18.0
2014-05-05 20:56:14 [2575] [INFO] Listening at: http://127.0.0.1:8000 (2575)
2014-05-05 20:56:14 [2575] [INFO] Using worker: sync
2014-05-05 20:56:14 [2584] [INFO] Booting worker with pid: 2584
```

However, when the '--preload' flag isn't used the environmental variable is available during application start up.

```
$ gunicorn -b localhost:8000 -e FOO=bar test:application
2014-05-05 21:06:00 [18244] [INFO] Starting gunicorn 18.0
2014-05-05 21:06:00 [18244] [INFO] Listening at: http://127.0.0.1:8000 (18244)
2014-05-05 21:06:00 [18244] [INFO] Using worker: sync
2014-05-05 21:06:00 [18249] [INFO] Booting worker with pid: 18249
This is the bootstrap. FOO = bar
```

When visiting 'localhost:8000' in a browser in both cases, the value of the 'FOO' variable is 'bar'.

```
$ curl http://localhost:8000/
The is the worker. FOO = bar
```

Is it expected behavior that environmental variables are not available during application start up when using the --preload flag?



Discussion:
I'm not aware of any good reason why it's this way.
@benoitc could we move the `# set environments variables` block in `Arbiter.start` into `Arbiter.setup`?

@tilgovi I don't see any reason coming to my mind actually... It's probably good to set it in the `setup` function.

@tilgovi @benoitc  Awesome. Thank you. Would you like a pull request?

@tshlabs :+1: :sailboat: 


Resolving patch:
diff --git a/gunicorn/arbiter.py b/gunicorn/arbiter.py
--- a/gunicorn/arbiter.py
+++ b/gunicorn/arbiter.py
@@ -106,6 +106,11 @@ def setup(self, app):
                 in sorted(self.cfg.settings.items(),
                           key=lambda setting: setting[1]))))
 
+        # set enviroment' variables
+        if self.cfg.env:
+            for k, v in self.cfg.env.items():
+                os.environ[k] = v
+
         if self.cfg.preload_app:
             self.app.wsgi()
 
@@ -121,11 +126,6 @@ def start(self):
             self.pidfile.create(self.pid)
         self.cfg.on_starting(self)
 
-        # set enviroment' variables
-        if self.cfg.env:
-            for k, v in self.cfg.env.items():
-                os.environ[k] = v
-
         self.init_signals()
         if not self.LISTENERS:
             self.LISTENERS = create_sockets(self.cfg, self.log)


Test patch:
diff --git a/tests/test_008-arbiter-env.py b/tests/test_008-arbiter-env.py
new file mode 100644
--- /dev/null
+++ b/tests/test_008-arbiter-env.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -
+#
+# This file is part of gunicorn released under the MIT license.
+# See the NOTICE for more information.
+
+import t
+import os
+from gunicorn.app.base import BaseApplication
+import gunicorn.arbiter
+
+
+class PreloadedAppWithEnvSettings(BaseApplication):
+    """
+    Simple application that makes use of the 'preload' feature to
+    start the application before spawning worker processes and sets
+    environmental variable configuration settings.
+    """
+
+    def init(self, parser, opts, args):
+        """No-op"""
+        pass
+
+    def load(self):
+        """No-op"""
+        pass
+
+    def load_config(self):
+        """Set the 'preload_app' and 'raw_env' settings in order to verify their
+        interaction below.
+        """
+        self.cfg.set('raw_env', [
+            'SOME_PATH=/tmp/something', 'OTHER_PATH=/tmp/something/else'])
+        self.cfg.set('preload_app', True)
+
+    def wsgi(self):
+        """Assert that the expected environmental variables are set when
+        the main entry point of this application is called as part of a
+        'preloaded' application.
+        """
+        verify_env_vars()
+        return super(PreloadedAppWithEnvSettings, self).wsgi()
+
+
+def verify_env_vars():
+    t.eq(os.getenv('SOME_PATH'), '/tmp/something')
+    t.eq(os.getenv('OTHER_PATH'), '/tmp/something/else')
+
+
+def test_env_vars_available_during_preload():
+    """Ensure that configured environmental variables are set during the
+    initial set up of the application (called from the .setup() method of
+    the Arbiter) such that they are available during the initial loading
+    of the WSGI application.
+    """
+    # Note that we aren't making any assertions here, they are made in the
+    # dummy application object being loaded here instead.
+    gunicorn.arbiter.Arbiter(PreloadedAppWithEnvSettings())